Reputation: 10193
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)?
Upvotes: 3
Views: 2942
Reputation: 24383
The ObjectDisposed
exception is probably because you have lazy loading turned on and you are disposing your context before trying to access customer.Orders
. I don't like lazy loading, so I always turn it off:
// in your context constructor:
this.Configuration.LazyLoadingEnabled = false;
Your original example should now work.
In your update, you are eagerly loading Customer.Orders
with the Include
clause. This means EF is getting this data from the database using a join
when it gets the Customer
record.
EDIT:
A DbContext
"remembers" every object it has seen, where an object is basically a database row.
If you load the Orders
associated with a particular Customer
then the context remembers those Order
objects.
When you subsequently fetch the Customer
object ( without .Include( "Orders" )
), EF is smart enough to attach the Order
objects you previously fetched to the Customer
object.
It can do this, because the Order
objects have a CustomerId
, so when you get that Customer
object it can look at its CustomerId
property and add the Order
objects to the Customer.Orders
collection.
It will only attach the Orders
it knows about, so if you haven't previously loaded all the associated Order
records for some reason, EF will just attach those it has seen.
Upvotes: 4