Entity Framework - Eager loading related entities
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.Re开发者_Python百科commendations)
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.
This is an old question, but in EF6 you can accomplish loading dependent objects on an entity like this:
context.Entry(seller).Collection(s => s.Recommendations).Query().Include(r => r.User)).Load();
That would load all Recommendations
and their related Users
for the given seller
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.
- Eager loading (using Include)
- Lazy loading
- Explicit loading by ObjectContext.LoadProperty (doesn't work for POCO)
There is no automatic loading.
I'm in the same situation. I think that with EF is very easy to fall in a 101 query problem.
A solution can be to create a partial class of your Seller class (generated by EF) and implement a GetSubclassNameQ that return a IQueryable, and a GetSubclassNameQFull that return a IQueryable with eager loading.
public partial class Seller{
public IQueryable<Recommendation> GetRecommendationsQ(EntityContainer entitycontainer) {
return entitycontainer.Recommendations;
}
public IQueryable<Recommendation> GetRecommendationsQFull(EntityContainer entitycontainer) {
return this.GetRecommendationsQ(entitycontainer).Include("Recommendations.User");
}
public IQueryable<Recommendation> GetRecommendationsQ() {
return GetRecommendationsQ(new EntityContainer());
}
public IQueryable<Recommendation> GetRecommendationsQFull() {
return this.GetRecommendationsQ().Include("Recommendations.User");
}
}
Rather than passing your actual domain objects (EntityObject
s) 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.
精彩评论