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

Entity framework 如何计算关联实体而不在实体框架中获取它们,entity-framework,count,Entity Framework,Count,我一直在想这一点有一段时间了,所以我认为值得使用我的第一篇堆栈溢出帖子来询问它 想象一下,我与一个相关的消息列表进行了讨论: DiscussionCategory discussionCategory = _repository.GetDiscussionCategory(id); discussionCategory.Discussions是当前未加载的讨论实体列表 我想要的是能够在discussionCategory中迭代讨论,并在不获取消息数据的情况下说出每个讨论中有多少消息 当我之前尝

我一直在想这一点有一段时间了,所以我认为值得使用我的第一篇堆栈溢出帖子来询问它

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

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);
    }
然后在视图中(只是为了测试它):


我希望这能让我的问题更清楚一点。如果您需要更多的代码详细信息,请告诉我。

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


我对你的问题投了赞成票并加了星星。希望有人能提出一个可行的解决方案。

简单;只需投影到POCO(或匿名)类型:

当您查看生成的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();

来源:

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

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

  • 创建数据库视图

    假设您需要所有原始实体属性加上关联的消息计数:

    CREATE VIEW DiscussionCategoryWithStats AS
    SELECT dc.*,
          (SELECT count(1) FROM Messages m WHERE m.DiscussionCategoryId = dc.Id)
              AS MessageCount
    FROM DiscussionCategory dc
    
    (如果您使用的是实体框架代码优先迁移,请参阅关于如何创建视图的部分。)

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

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

  • 我知道这是一个老生常谈的问题,但它似乎是一个持续存在的问题,上面的答案都不能提供一个很好的方法来处理列表视图中的SQL聚合

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

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

  • 使用
    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; }
        }
    }
    
  • 在字典中记录计数。这将生成一个包含所有父ID和子对象计数的SQL GROUP BY查询。(假定
    DiscussionID
    消息中的FK

  • 循环父对象,从字典中查找计数,并存储在占位符属性中

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

    return View(discussions);
    
  • 参考视图中的
    MessageCount
    属性

    @foreach (var item in Model) {
        ...
        @item.MessageCount
        ...
    }
    
  • 是的,您可以将字典塞进ViewBag中,并直接在视图中进行查找,但这会用不需要的代码混淆视图

    最后,我希望EF能有一种“懒惰计数”的方法。延迟加载和显式加载的问题在于您正在加载对象。如果必须加载才能计数,则这是一个潜在的性能问题。延迟计数无法解决列表视图中的n+1问题,但如果能够从视图中调用
    @item.Messages.Count
    ,而不必担心可能会加载大量不需要的对象数据,那当然很好


    希望这有帮助。

    您好,谢谢您的回答。不幸的是,我以前尝试过这个方法,发现匿名类型的count属性在所有讨论中都返回了零。在看到你的答案后,我又尝试了一次这种方法,但结果是一样的。就保持实体与数据存储的“连接”而言,我可能误解了框架的某些内容。其他人能否确认上述方法有效?显示您的代码;你做错了什么。我们在运输应用程序中广泛使用此功能。我怀疑您试图在EF关联属性(如您的问题中)而不是L2E查询(如我的回答中)上执行此操作。那是完全不同的,;前者是对象的LINQ;后者是LINQ到实体
    var _counts = db.Messages.GroupBy(m => m.DiscussionID).ToDictionary(d => d.Key, d => d.Count());
    
    foreach (var d in discussions)
        {
            d.MessageCount = (_counts.ContainsKey(d.ID)) ? _counts[d.ID] : 0;
        }
    
    return View(discussions);
    
    @foreach (var item in Model) {
        ...
        @item.MessageCount
        ...
    }