views:

122

answers:

3

I'm using Entity Framework 4 with MVC and need to ensure any referenced entities I want to use in my view have been loaded before the controller method returns, otherwise the view spits out the dreaded:

The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

When selecting straight from the context, I can just use the Include(string) method to force them to be included in the generated SQL query:

var sellers = context.Sellers.Include("Recommendations.User").ToList();

However, if I have (for example) a helper method that accepts an entity and needs all items to be loaded, there's no Include method available.

void Test(Seller seller)
{
    // ensure all recommendations and their users are loaded
}

The brute force approach is to loop through them:

foreach (var recommendation in seller.Recommendations)
    recommendation.User.ToString(); // just force a load

If I have 100 recommendations, this will create 101 SQL queries behind-the-scenes. Ideally I want a method/approach that loads all Recommendation AND User objects with only a single trip to SQL.

Show me the money.

EDIT I'm not really interested in discussing whether this is a good or bad architecture. I've simplified my scenario for the sake of the question. Can you do what I'm asking with the EF API?

EDIT 2

Ladislav's edit offered hope of a new approach, but it seems I'm not quite there.

I can achieve what I want via this:

context.Sellers.Include("Recommendations.User").Single(s => s.Id == seller.Id);

This approach doesn't work using LoadProperty...

context.LoadProperty(seller, "Recommendations.User");

...as it fails with the error...

The specified navigation property Recommendations.User could not be found.

Neither of these approaches work if you don't have a reference to the context.

+1  A: 

I think this is a job for your repository which should in your case expose methods like GetFullSeller (all properties loaded by Include) and GetSeller (only base entity).

Edit:

There are several ways how to load navigation properties in EF v4.

There is no automatic loading.

Ladislav Mrnka
@Ladislav, does that mean you would only ever work on an object that comes straight from a repository? What if you received an entity over, say, WCF, then wanted to attach it and populate more fields on it in order to do more transformations before ultimately saving it? Anyway, I don't really see this as an architecture question. I just want to know if the API supports this, and if not, why not.
Drew Noakes
A: 

Rather than passing your actual domain objects (EntityObjects) to the view, you may want to use your controller to map them into a Model object that better represents what your view should actually be displaying. This will reduce the amount of logic required in your View, and have the pleasant side-effect of avoiding passing EntityObjects around after their context has expired.

Edit based on your edit:

No, the API doesn't have a way to take a single Entity Object and make every other Entity Object of its type which was loaded at the same time it was be lazy-populated with a particular property in one fell swoop. You are better off pulling all of the items out in the first place using the Include mention like you show in your question.

StriplingWarrior
@StriplingWarrior, thanks for your answer. I've simplified my case for the sake of clarity in the question. I do actually use a 'view model' such that there is hardly any logic in my views. My model is deeply interlinked with relationships and I don't want to recreate that hierarchy in view models, so I have a base-level view model that has some entity enumerations on them. In my case, I need to dereference another entity on those in an enumeration. This isn't really an architecture question, so much as an API one.
Drew Noakes
@StriplingWarrior -- based on your edit ;) That's not quite what I want to do. I have a seller. I need all of the seller's recommendations to be loaded. On each of those recommendations, I need the corresponding user to be loaded. I don't need any other sellers to be loaded.
Drew Noakes
If I could invent the API, I would say `seller.Load("Recommendations.User")`, just as I could do in the original entity set query (as in OP.)
Drew Noakes
I understand what you mean, now. That could be pretty handy. Unfortunately, the "include" functionality is very intrinsically tied to the `ObjectQuery<T>` object (context.Sellers), and there is no API to permit what you're asking for on the `EntityCollection<T>` object (seller.Recommendations).
StriplingWarrior