« Team Build Error ASPPARSE: Could not load type | Main | New CRM Developer Book »

Entity Framework - Cast Affects Include

I got dragged into looking at a problem today with Entity Framework where .Include("...") was used to eager load related data and it wasn't working. The included data was never retrieved - however, if you explicitly did a .Load() on the reference it loaded fine on the fly. So clearly the model fundamentals seemed ok, however something just wasn't right.

As it turned out after pulling a few hairs out, that I was clearly overlooking the obvious.  At first glance the query looked perfectly normal, so much so that again I overlooked the obvious.  After isolating the model elements to a new .edmx file and not finding the culprit.  Because I love challenges like that, I refused to give up until I finally found what was causing it.  The real story involved custom entities that had nothing to do with AdventureWorks but to keep it simple here (and also not show any private stuff!) I will use adventure works for the example.

First the Query in question

AdventureWorksLTEntities ctx = new AdventureWorksLTEntities();

var query = from SalesOrderHeader order in ctx.SalesOrderHeader.Include("Customer")
            select order;
query.First();

Running this and looking at the ToTraceString() reveals that no join is done with Customer despite the explicit Include("Customer") being specified.  In the real example, I got the query that I was testing from the developer and never modified it.  In my testing, I ended up retyping it and leaving off one item of the query.  The query I found that worked looked like the following

var query = from  order in ctx.SalesOrderHeader.Include("Customer")
                      select order;

query.First();

At first glance you might not notice what I removed - but I removed the SalesOrderHeader as marked in the following query.

var query = from SalesOrderHeader order in ctx.SalesOrderHeader.Include("Customer")
            select order;

Ok, now my first reaction is that makes NO sense....and it was the fact that lunch time had passed and I hadn't eaten was causing crazy results. Not a good enough answer....so I dug a little more.  I believed in both queries really generated the same code only when the SalesOrderHeader was omitted it would be inferred.

As it turns out reality is not always as one thinks, so what I found was a Cast<> was inserted in the flow of the methods when this was expanded by the compiler.  

With SalesOrderHeader specified explicitly here's the code generated at compilation

ctx.SalesOrderHeader.Include("Customer").Cast<SalesOrderHeader>().Select<SalesOrderHeader, SalesOrderHeader>(Expression.Lambda...all the same after

Compare that to the code without SalesOrderHeader and you can see that the Cast<>() is inserted above.

ctx.SalesOrderHeader.Include("Customer").Select<SalesOrderHeader, SalesOrderHeader>(Expression.Lambda...all the same after

That insertion of the Cast appears to completely negate the effect of the .Include("Customer") like it was never specified. 

I later tried adding the .Include after the initial query using the following logic

query = ((ObjectQuery<SalesOrderHeader>)query).Include("Customer");

The result of that was that the .Include worked fine and the results of the query were as expected.

At first, I thought this might be a bug, but I'm more inclined to believe it was behavior by design to support the explicit setting of the type of the item.  Maybe someone on the EF team can share the thoughts behind the behavior when the type is specified. 

For now, I will chalk it up at least as an undesirable behavior since it was a pain to track down.  Logical thought was that both should have returned the same results.  So next time you don't see a join occurring for related data keep this one in mind!

Posted on Wednesday, August 20, 2008 at 09:44PM by Registered CommenterDavid Yack | CommentsPost a Comment

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>