如何计算关联实体而不在entity framework中获取它们

我一直在思考这个问题,所以我认为这将是值得使用我的第一个堆栈溢出后询问。

想象一下,我有一个关联消息列表的讨论:

DiscussionCategory discussionCategory = _repository.GetDiscussionCategory(id); 

discussionCategory.Discussions是当前未加载的讨论实体列表。

我想要的是能够迭代讨论类别中的讨论,并说出每个讨论中有多less个消息,而不用提取消息数据。

当我必须加载讨论和消息之前,我已经尝试了这个,所以我可以做这样的事情:

 discussionCategory.Discussions.Attach(Model.Discussions.CreateSourceQuery().Include("Messages").AsEnumerable()); foreach(Discussion discussion in discussionCategory.Discussions) { int messageCount = discussion.Messages.Count; Console.WriteLine(messageCount); } 

这对我来说似乎相当低效,因为我从数据库中获取数百个可能的消息体,并将其保留在内存中,当我只想为表示目的计数它们的数量时。

我看到了一些涉及这个问题的问题,但他们似乎没有直接解决这个问题。

提前感谢您对这个主题的任何想法。

更新 – 一些更多的代码请求:

 public ActionResult Details(int id) { Project project = _repository.GetProject(id); return View(project); } 

然后在视图中(只是为了testing):

 Model.Discussions.Load(); var items = from d in Model.Discussions select new { Id = d.Id, Name = d.Name, MessageCount = d.Messages.Count() }; foreach (var item in items) { //etc 

我希望能让我的问题更清楚些。 让我知道你是否需要更多的代码细节。

简单; 只需投射到POCO(或匿名)types:

 var q = from d in Model.Discussions select new DiscussionPresentation { Subject = d.Subject, MessageCount = d.Messages.Count(), }; 

当您查看生成的SQL时,您将看到Count()由DB服务器完成。

请注意,这适用于EF 1和EF 4。

如果您使用Entity Framework 4.1或更高版本,则可以使用:

 var discussion = _repository.GetDiscussionCategory(id); // Count how many messages the discussion has var messageCount = context.Entry(discussion) .Collection(d => d.Messages) .Query() .Count(); 

资料来源: http : //msdn.microsoft.com/en-US/data/jj574232

我知道这是一个老问题,但似乎是一个持续的问题,上面的答案都没有提供一个好的方法来处理列表视图中的SQL聚合。

我假设直接POCO模型和Code First就像在模板和示例中一样。 虽然从数据库pipe理员的angular度来看,SQL View解决scheme非常好,但是它重新提出了同时维护代码和数据库结构的挑战。 对于简单的SQL聚合查询,您不会从View中看到很多速度增益。 你真正需要避免的是多个(n + 1)数据库查询,如上例所示。 如果您有5000个父实体,并且您计算子实体(例如,每个讨论的消息),那就是5001个SQL查询。

您可以在单个SQL查询中返回所有这些计数。 就是这样。

  1. 使用System.ComponentModel.DataAnnotations.Schema命名空间中的[NotMapped]数据注释向您的类模型添加占位符属性。 这为您提供了一个存储计算数据的地方,而不需要将实际的列添加到数据库或投影到不必要的视图模型。

     ... using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace MyProject.Models { public class Discussion { [Key] public int ID { get; set; } ... [NotMapped] public int MessageCount { get; set; } public virtual ICollection<Message> Messages { get; set; } } } 
  2. 在您的Controller中,获取父对象的列表。

     var discussions = db.Discussions.ToList(); 
  3. 捕获字典中的计数。 这将生成一个包含所有父ID和子对象计数的SQL GROUP BY查询。 (假定DiscussionIDMessages的FK。)

     var _counts = db.Messages.GroupBy(m => m.DiscussionID).ToDictionary(d => d.Key, d => d.Count()); 
  4. 循环浏览父对象,从字典中查找计数,并存储在占位符属性中。

     foreach (var d in discussions) { d.MessageCount = (_counts.ContainsKey(d.ID)) ? _counts[d.ID] : 0; } 
  5. 返回您的讨论列表。

     return View(discussions); 
  6. 在视图中引用MessageCount属性。

     @foreach (var item in Model) { ... @item.MessageCount ... } 

是的,你可以将这个字典填充到ViewBag中,直接在View中进行查找,但是用不需要在那里的代码来混淆你的视图。

最后,我希望EF有办法做“懒计”。 加载惰性和显式加载的问题是你正在加载对象。 如果你必须加载计数,这是一个潜在的性能问题。 延迟计数并不能解决列表视图中的n + 1问题,但是它肯定能很好地从View中调用@item.Messages.Count ,而不必担心可能会加载大量不需要的对象数据。

希望这可以帮助。

我没有一个直接的答案,但只能指出你以下NHibernate和EF 4.0之间的比较,这似乎表明,即使在EF 4.0中没有支持获取相关的实体集合的计数没有检索采集。

http://ayende.com/Blog/archive/2010/01/05/nhibernate-vs.-entity-framework-4.0.aspx

我已经upvoted和主演你的问题。 希望有人会用一个可行的解决办法。

在处理包括EF和DevExpress XPO(甚至不允许单个实体映射到多个表)的多个映射器时,我遇到了同样的问题。 我发现最好的解决scheme是基本上使用EDMX和T4模板在SQL Server中生成可更新的视图(而不是触发器),这样你就可以对sql进行低级别的控制,所以你可以在select中进行子查询子句,使用各种复杂的联接来引入数据等等。

如果这不是一次性的,而且你发现自己需要统计许多不同的关联实体,那么数据库视图可能是一个更简单的(也是更合适的)select:

  1. 创build你的数据库视图。

    假设你想要所有的原始实体属性加上相关的消息数量:

     CREATE VIEW DiscussionCategoryWithStats AS SELECT dc.*, (SELECT count(1) FROM Messages m WHERE m.DiscussionCategoryId = dc.Id) AS MessageCount FROM DiscussionCategory dc 

    (如果您使用的是entity framework代码优先迁移,请参阅此答案以了解如何创build视图。)

  2. 在EF中,只需使用视图而不是原始实体:

     // You'll need to implement this! DiscussionCategoryWithStats dcs = _repository.GetDiscussionCategoryWithStats(id); int i = dcs.MessageCount; ...