Tip 22 - How to make Include really Include

This is 22nd post in my ongoing series of Entity Framework Tips.

If you want to do eager loading with the Entity Framework it is generally really easy, you simply write something like this:

var results = 
from post in ctx.Posts.Include(“Comments”)
where post.Author.EmailAddress == “alexj@microsoft.com
select post;

In this example for each post that matches you will get back it’s comments, all in one query, aka Eager loading.

Cool. So far so good.

However if you start to do more interesting queries that change the ‘shape’ of the query, either by introducing an intermediary join or something, maybe like this:

var results =
from post in ctx.Posts.Include(“Comments”)
from blog in post.Blogs
where blog.Owner.EmailAddress == “alexj@microsoft.com
select post;

The include no longer works.

Why? 

When the first example is translated into a query the columns that are selected remain the same from start to end. The filter introduces a join but it doesn’t effect which columns are selected, so from the time the Include() is applied until the final select the shape of the query is unchanged.

In the second example when the Include() is applied the query includes just post columns, but then the second from changes the shape to include both post and blog columns. i.e. the shape of results changes, albeit temporarily, and this temporary shape change stops Include() working.

Now in this particular example you can re-write the query like this, to get it working again:

var results =
from post in ctx.Posts.Include(“Comments”)
where post.Blogs.Any(
b => b.Owner.EmailAddress == “alexj@microsoft.com
)
select post;

…and it will work again. Because the query has been rewritten to avoid changing the shape, from the time the Include() is applied until the end.

Unfortunately this type of workaround though change is somewhat invasive because it forces you to change how you write your queries just to get include working.

There is however a better option.

Workaround

The workaround is actually pretty simple, you just move the Include to the end of the query.

var results =
((from post in ctx.Posts
from blog in post.Blogs
where blog.Owner.EmailAddress == “alexj@microsoft.com
select post) as ObjectQuery<Post>).Include(“Comments”);

For this to work your final select must be entities, i.e. select post rather than select new {…}, if so you can cast the result to an ObjectQuery, and then do the include.

This works just fine, because between when the include is applied and the end of the query the shape remains the same.