I need to retrieve a large hierarchy of entities from my database, with many relationships. Rather than create one giant queries with many Includes, I’ve read that it’s possible to use a number of smaller queries to fetch different parts of the hierarchy, then EF somehow glues everything together using something called “association fixup”. However I’m struggling to get it to work. (I’m using EF5 and POCO by the way).
As a simple example, I’m trying to retrieve a Customer and all their related Orders using the “fixup” technique. This is what I’m doing in my BLL tier:-
var customer = context.Customers
.Where(o => o.Id == requestedCustomerId).SingleOrDefault();
customer.Orders = context.Orders
.Where(o => o.CustomerId = requestedCustomerId).ToList();
When I examine the customer entity returned to the UI tier, customer.Orders gives an ObjectDisposedException. What am I doing wrong?
As a further example of how fixup would be used, how would I populate the Orders’ related OrderLines (either in a separate query, or as part of the second query above)? And what if the OrderLine entity had a ProductCategory parent entity – how would I populate these?
Update
I’ve just tried the following, which works:-
var orders = context.Orders
.Where(o => o.CustomerId = requestedCustomerId).ToList();
var customer = context.Customers
.Where(o => o.Id == requestedCustomerId)
.Include("Orders")
.SingleOrDefault();
Am I right in saying that the second query won’t fetch Orders from the database again, and EF will associate those retrieved by the previous query (even though they are just sitting in some arbitrary variable)?
The
ObjectDisposedexception is probably because you have lazy loading turned on and you are disposing your context before trying to accesscustomer.Orders. I don’t like lazy loading, so I always turn it off:Your original example should now work.
In your update, you are eagerly loading
Customer.Orderswith theIncludeclause. This means EF is getting this data from the database using ajoinwhen it gets theCustomerrecord.EDIT:
A
DbContext“remembers” every object it has seen, where an object is basically a database row.If you load the
Ordersassociated with a particularCustomerthen the context remembers thoseOrderobjects.When you subsequently fetch the
Customerobject ( without.Include( "Orders" )), EF is smart enough to attach theOrderobjects you previously fetched to theCustomerobject.It can do this, because the
Orderobjects have aCustomerId, so when you get thatCustomerobject it can look at itsCustomerIdproperty and add theOrderobjects to theCustomer.Orderscollection.It will only attach the
Ordersit knows about, so if you haven’t previously loaded all the associatedOrderrecords for some reason, EF will just attach those it has seen.