Andrew Stephens
Andrew Stephens

Reputation: 10193

How do I use Entity Framework association "fixup"?

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

Answers (1)

Nick Butler
Nick Butler

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

Related Questions