如何计算相关实体而不在实体框架中获取它们

时间:2010-01-06 04:07:27

标签: entity-framework count

我一直想知道这个问题,所以我认为使用我的第一个Stack Overflow帖子来询问它是值得的。

想象一下,我与相关的消息列表进行了讨论:

DiscussionCategory discussionCategory = _repository.GetDiscussionCategory(id);

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

我想要的是能够遍历discussionCategory中的讨论并说明每次讨论中有多少消息而不提取消息数据。

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

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

然后在视图中(只是为了测试它):

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

我希望这会让我的问题更加清晰。如果您需要更多代码详细信息,请与我们联系。

6 个答案:

答案 0 :(得分:38)

易于;只需投射到POCO(或匿名)类型:

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

当您查看生成的SQL时,您会看到Count()由数据库服务器完成。

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

答案 1 :(得分:10)

如果您使用的是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

答案 2 :(得分:5)

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

我在模板和示例中假设直接POCO模型和Code First。虽然从DBA的角度来看,SQL View解决方案很不错,但它重新引入了并行维护代码和数据库结构的挑战。对于简单的SQL聚合查询,您不会从View中看到很多速度增益。您真正需要避免的是多个(n + 1)数据库查询,如上例所示。如果您有5000个父实体并且您正在计算子实体(例如,每次讨论的消息),那就是5001个SQL查询。

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

  1. 使用[NotMapped]命名空间中的System.ComponentModel.DataAnnotations.Schema数据注释向您的类模型添加占位符属性。这使您可以存储计算数据,而无需实际向数据库添加列或投影到不必要的View模型。

    ...
    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
        ...
    }
    
  7. 是的,您可以将该词典填充到ViewBag中并直接在View中进行查找,但这会使用不需要的代码混淆您的视图。

    最后,我希望EF有办法做到&#34;懒惰计数&#34;。延迟加载和显式加载的问题是您加载对象。如果你需要加载计数,那就是潜在的性能问题。懒惰计数不会解决列表视图中的n + 1问题,但是能够从View中调用@item.Messages.Count而不必担心可能会加载大量不需要的对象数据肯定会很好。< / p>

    希望这有帮助。

答案 3 :(得分:0)

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

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

我已经投票并主演了你的问题。希望有人会提出一个可行的解决方案。

答案 4 :(得分:0)

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

答案 5 :(得分:0)

如果这不是一次性的,并且您发现自己需要计算许多不同的关联实体,那么数据库视图可能是更简单(并且可能更合适)的选择:

  1. 创建数据库视图。

    假设您想要所有原始实体属性以及相关的消息计数:

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

    (如果您使用的是实体框架代码优先迁移,请参阅this SO answer了解如何创建视图。)

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

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