实体框架 - 预加载相关实体

13
我正在使用Entity Framework 4和MVC,需要确保在控制器方法返回之前加载任何要在视图中使用的引用实体,否则视图会输出可怕的错误信息:
“ObjectContext实例已被处理并且不能再用于需要连接的操作。”
当直接从上下文中选择时,我可以使用Include(string)方法来强制它们包含在生成的SQL查询中:
var sellers = context.Sellers.Include("Recommendations.User").ToList();

然而,如果我有一个帮助方法,它接受一个实体并需要加载所有项,那么就没有可用的Include方法。

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

粗暴的方法是通过循环来遍历它们:
foreach (var recommendation in seller.Recommendations)
    recommendation.User.ToString(); // just force a load

如果我有100个推荐,这将在幕后创建101个SQL查询。理想情况下,我希望有一种方法/方法,可以仅通过一次访问SQL来加载所有RecommendationUser对象。
给我看钱。
编辑 我并不是真的对讨论这是否是一个好或坏的架构感兴趣。为了问题的简化,我已经简化了我的场景。您能否使用EF API做我要求的事情?
编辑2 Ladislav's edit 提供了新方法的希望,但似乎我还没有到达目标。我可以通过以下方式实现我想要的:
context.Sellers.Include("Recommendations.User").Single(s => s.Id == seller.Id);

使用LoadProperty的方法并不起作用...

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

...由于出现了错误,无法继续运行...

无法找到指定的导航属性Recommendations.User。

如果没有对上下文的引用,则这些方法都无法正常工作。


虽然这并没有回答你的问题(因此我将其作为评论发布),但它确实讨论了在EF6中当前如何加载相关实体。此外,这是在Google搜索“entity framework load related entities”时出现的第一个SOF链接:http://msdn.microsoft.com/en-us/data/jj574232。所以我想分享一下... - Zar Shardan
4个回答

5

这是一个老问题,但在EF6中,您可以像这样加载实体上的相关对象:

context.Entry(seller).Collection(s => s.Recommendations).Query().Include(r => r.User)).Load();

这将会加载给定 卖家 的所有推荐及其相关的用户


3
我认为这是你的代码库应该处理的工作,在你的情况下,它应该公开像GetFullSeller(通过Include加载所有属性)和GetSeller(只有基本实体)这样的方法。
编辑:
在EF v4中,有几种加载导航属性的方式:
- 急切加载(使用Include) - 延迟加载 - 通过ObjectContext.LoadProperty显式加载(不适用于POCO)
没有自动加载。

1
这是否意味着您只会处理直接来自存储库的对象?如果您收到一个实体,比如通过WCF,然后想要附加它并填充更多字段以便在最终保存之前进行更多转换,那该怎么办?无论如何,我并不认为这是一个架构问题。我只是想知道API是否支持此功能,如果不支持,为什么不支持。 - Drew Noakes

2

我也处于同样的情况。我认为使用EF很容易遇到101查询问题。

解决方案是创建你的Seller类的部分类(由EF生成),并实现一个返回IQueryable的GetSubclassNameQ方法和一个返回带有贪婪加载的IQueryable的GetSubclassNameQFull方法。

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");
  }

}

0

与其将实际的域对象(EntityObject)传递给视图,您可能希望使用控制器将它们映射到更好地表示视图应该显示的模型对象中。这将减少视图中所需的逻辑量,并愉快地避免在上下文过期后传递EntityObjects的副作用。

根据您的编辑进行编辑:

不,API没有一种方法可以接受单个实体对象并使每个同时加载的同类型实体对象都懒惰地填充一个特定属性。最好使用像您在问题中显示的Include提及的方式首先将所有项目全部取出。


感谢您的回答。为了在问题中更清晰地表达,我已经简化了我的情况。实际上,我确实使用了“视图模型”,以便我的视图中几乎没有任何逻辑。我的模型与关系深度交织,我不想在视图模型中重新创建该层次结构,因此我有一个基本级别的视图模型,在其中有一些实体枚举。在我的情况下,我需要对枚举中的另一个实体进行解引用。这不是一个架构问题,而是一个API问题。 - Drew Noakes
根据您的编辑 ;) 这不是我想要做的。我有一个卖家。我需要加载所有卖家的推荐。在这些推荐中,我需要加载相应的用户。我不需要加载任何其他卖家。 - Drew Noakes
如果我能发明API,我会说seller.Load("Recommendations.User"),就像在原始实体集查询中一样(如OP中所示)。 - Drew Noakes
我现在明白你的意思了。那确实很方便。不幸的是,“include”功能与ObjectQuery<T>对象(context.Sellers)非常紧密地绑定在一起,而且在EntityCollection<T>对象(seller.Recommendations)上没有允许你所要求的API。 - StriplingWarrior

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接