References not eagerly fetched are lost
If you don't explicitly ask for related entities when you load an entity, you won't be able to do it later.
Problem
Object-Relational Mappers (ORMs) typically have a feature called lazy loading. Lazy loading means that when an entity is loaded from the database, all its relations (one-to-one, one-to-many, many-to-one, many-to-many) do not necessarily need to be loaded too. When they are first accessed, the ORM will issue a query and retrieve the records. This is interesting, because we do not need to pay the extra cost of loading potentially a lot of records if we are not going to use them. On the other hand, it requires that a connection is always available when the lazy properties are first accessed, otherwise their data cannot be retrieved.
So, here's the catch: Entity Framework Core 1.0 does not have lazy loading. It will come in a future version. What happens, then? Let's imagine we have the following class model:
using System; using System.Collections.Generic; namespace BusinessLogic { public class Blog { public int BlogId { get; set; } public string Name { get; set; } public DateTime CreationDate { get; set; } public string Url { get; set; } public ICollection<Post> Posts { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public DateTime Timestamp { get; set; } public string Body { get; set; } public Blog Blog { get; set; } public ICollection<Comment> Comments { get; set; } } }
See what happens if you execute a query for the Blog
class, such as this one:
var blogs = ctx.Blogs.ToList();
You will retrieve all the blogs in the database, but on each of them, the Posts
collection will be null, even for those blogs that do have posts! This is an example of a not loaded one-to-many relationship.
How to solve it…
This is caused by the lack of the lazy loading feature. Since Entity Framework wasn't instructed to fetch the associated posts, it won't do it. The solution is to eagerly fetch them in the query:
var blogsWithPosts = ctx .Blogs .Include(b => b.Posts) .ToList(); This way, all of the posts will be retrieved alongside the blogs.
The same will happen for many-to-one relationships, asking for all posts will not bring along their blogs:
var posts = ctx.Posts.ToList();
We also need to explicitly ask for the Blog
property to be included:
var postsWithBlogs = ctx .Posts .Include(p => p.Blog) .ToList();
If you look at the SQL query that was produced, you will see that it generates a LEFT JOIN
clause, bringing together the columns for both the Blog
and Posts
tables.
The Include
extension method can even be used to eagerly load multiple levels, for example:
var postsWithBlogsAndComments = ctx .Posts .Include(p => p.Blog) .Include(p => p.Comments) .ToList();
Note
Not asking for a relation to be retrieved will cause its corresponding property to be null, so be aware of it.