2010-01-06 27 views
39

我一直在想這一段時間,所以我認爲這將是值得使用我的第一個堆棧溢出後詢問它。如何計算相關實體,而不需要在實體框架中獲取它們

想象我有消息的關聯列表的討論:

DiscussionCategory discussionCategory = _repository.GetDiscussionCategory(id); 

discussionCategory.Discussions是當前未加載討論實體的名單。

我想要的是能夠遍歷討論類別中的討論,並說出每次討論中有多少消息不需要獲取消息數據。

當我嘗試這樣做之前,我不得不加載討論和消息,這樣我可以做這樣的事情:

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 

我希望這是我的問題更清楚一點。讓我知道你是否需要更多的代碼細節。

回答

35

簡單;只是project到POCO(或匿名)類型:

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

當你看到生成的SQL,你會看到Count()由DB服務器完成。

注意這兩個EF 1和EF 4

+0

嗨,謝謝你的回答。不幸的是,我之前嘗試過,發現匿名類型的count屬性對於所有討論都返回零。看到你的答案後,我再次嘗試這種方法,但結果相同。 也許我誤解了有關框架的一些問題,只要保持實體與數據存儲「連接」即可。 其他人可以確認上述方法應該工作嗎? – Oligarchia 2010-01-06 14:47:54

+0

顯示您的代碼;你做錯了什麼。我們在我們的運輸應用中廣泛使用此功能*。我懷疑你試圖在EF關聯屬性(如你的問題)而不是在L2E查詢(如我的答案)。那些完全不同;前者是LINQ to Objects;後者是LINQ to Entities。 – 2010-01-06 14:52:23

+0

我使用的代碼在形式上與您提供的代碼相同。也許有更進一步的環節出了問題。如果您認爲有幫助,我會查看是否可以編輯原始帖子以提供其他代碼。再次感謝你的幫助。我正在查看您現在鏈接到的博客文章。 – Oligarchia 2010-01-06 15:02:18

0

我沒有一個直接的答案,但只能指出你在NHibernate和EF 4.0之間的比較,這似乎表明,即使在EF 4.0中也沒有開箱即用的支持獲取相關實體的計數收集而不檢索收藏。

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

我upvoted出演你的問題。希望有人會用一個可行的解決方案。

+0

這對EF的每個版本都是錯誤的。看到我的答案。 – 2010-01-06 14:46:11

+0

這很好。就像我說過的,我沒有直接回答他的問題,但網絡上有很多東西說要計數是困難的或不可能的。當他們說一些事情是不可能的時候,我並不傾向於相信某人,但我想把它呈現給提問者並且開始討論。 – 2010-01-06 14:51:12

10

如果您正在使用實體框架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

0

與多個映射器包括EF和的DevExpress XPO(打交道時,我遇到了同樣的問題,它甚至不允許一個單一的實體映射到多個表)。我發現最好的解決方案是基本上使用EDMX和T4模板在SQL Server中生成可更新的視圖(而不是觸發器),這樣你就可以對sql進行低級別的控制,因此你可以在select中進行子查詢子句,使用各種複雜的聯接來引入數據等等。

0

如果這不是一次性的,你會發現自己需要計算大量不同的相關實體,數據庫視圖可能是一個更簡單的(而且可能更合適)的選擇:

  1. 創建你的數據庫視圖。

    假設你希望所有的原始實體屬性,再加上相關的消息計數:(如果你正在使用實體框架代碼第一次遷移,請參閱如何創建一個視圖this SO answer

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

  2. 在EF,只需使用視圖,而不是原來的實體:

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

我知道這是一個老問題,但它似乎是一個持續存在的問題,上面的答案都沒有提供處理列表視圖中的SQL聚合的好方法。

我假設直接POCO模型和Code First就像在模板和示例中一樣。雖然從數據庫管理員的角度來看,SQL View解決方案很好,但它重新引入了並行維護代碼和數據庫結構的挑戰。對於簡單的SQL聚合查詢,您不會從View中看到很多速度增益。你真正需要避免的是多個(n + 1)數據庫查詢,如上例所示。如果您有5000個父實體並且您計算子實體(例如每個討論的消息),那就是5001個SQL查詢。

您可以在單個SQL查詢中返回所有這些計數。就是這樣。

  1. 使用[NotMapped]數據註解來自System.ComponentModel.DataAnnotations.Schema命名空間中的佔位符屬性添加到您的類模型。這爲您提供了一個存儲計算數據的地方,而無需向數據庫實際添加列或投影到不必要的視圖模型。

    ... 
    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,直接做查找中查看,但muddies的代碼並不需要在那裏你的看法。

最後,我希望EF有辦法做「懶計數」。加載惰性和顯式加載的問題是您正在加載對象的。如果你必須加載計數,這是一個潛在的性能問題。延遲計數並不能解決列表視圖中的n + 1問題,但它肯定會很高興能夠從視圖中調用@item.Messages.Count,而不必擔心可能會加載大量不需要的對象數據。

希望這會有所幫助。