2014-04-01 60 views
0

我是新來的.Net,ASP,實體框架和Linq的世界,所以忍受着我...在模型上正確實現IQueryable作爲成員變量?

我最初有一個模型設置如下;

public class Pad 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public Guid PadId { get; set; } 
    public string StreetAddress { get; set; } 
    public int ZipCode { get; set; } 
    public virtual ICollection<Mate> Mates { get; set; } 
    public virtual ICollection<Message> Messages { get; set; } 
} 

(A墊是一個聊天室 - 它包含了許多隊友和數以萬計的消息) 在Web API控制器,我設計獲得從指定的墊25個最近的消息的功能。

public IHttpActionResult GetMessages(string id) 
{ 
    var padGuid = new Guid(id); 

    // Try to find the pad referenced by the passed ID 
    var pads = (from p in db.Pads 
       where p.PadId == padGuid 
       select p); 
    if (pads.Count() <= 0) 
    { 
     return NotFound(); 
    } 
    var pad = pads.First(); 

    // Grab the last 25 messages in this pad. 
    // PERFORMANCE PROBLEM 
    var messages = pad.Messages.OrderBy(c => c.SendTime).Skip(Math.Max(0, pad.Messages.Count() - 25)); 
    var messagesmodel = from m in messages 
      select m.toViewModel(); 
    return Ok(messagesmodel); 
} 

這個實現的問題是,它好像EF加載整組消息(數千個)的入內存得到計數,排序等,從而產生了巨大的性能損失之前有很多消息的墊子。

我首先想到的是把Pad.Messages成員類型轉換爲IQueryable而不是ICollection - 這應該推遲LINQ查詢到SQL,我這樣想着。然而,在這樣做的時候,上面的功能就像pad.Messages.Count()抱怨 - 原來pad.Messages是空值!並在其他地方中斷,例如將Messages添加到Pad.Messages的值。

什麼是這樣的正確實施?在其他地方,我已經看到推薦的解決方案是針對上下文構建第二個查詢,如select Messages where PadId = n,但是當我可以使用Messages成員值時,這看起來似乎很直觀。

謝謝!

+0

你理解正確。這是EF在這種情況下的表現。 –

+0

並非如此@TMcKeown您可以使用LINQ以避免遇到此問題。看到我的答案。 –

+2

爲什麼不通過SendTime降序訂購25個元素?在記憶中,你總是可以重新命令它們再次上升。 – Dismissile

回答

1
var messages = db.Pads.Where(p => p.PadId == padGuid) 
    .SelectMany(pad => p.Messages.OrderBy(c => c.SendTime) 
     .Skip(Math.Max(0, pad.Messages.Count() - 25))); 
+0

你期望從哪裏得到'pad'? – Servy

+0

修正了疏忽。有點難,沒有intellisense :) –

+0

啊,我看到 - 所以,而不是使用pad.Messages本身和運行額外的查詢,我構建另一個Linq查詢完全。這在實踐中是典型的嗎? –

-2

如何計劃在沒有實際執行數據庫查詢的情況下對數據庫查詢中的結果數進行計數?

您如何計劃在查詢中獲得第一個項目而不實際執行查詢?

你不能做;兩者都必須執行查詢。

+0

我不完全熟悉IQueryable如何與Linq一起工作。我認爲它會返回某種'預加載'的查詢,我可以在其他Linq操作中使用這些查詢。 –

+0

@HaydenMcAfee是的,它的確如此。 Count計數返回什麼? 'First'返回了什麼?那些返回'IQueryable'對象中的任何一個?如果是的話,那麼他們可以推遲執行,否則他們不能。 – Servy

+0

在將'Pad.Messages'設置爲'IQueryable'的情況下,我的問題似乎是'pad = pads.First()'在某種程度上將得到的'Pad'對象展平,所以'pad.Messages'一個空值,導致錯誤調用「Count」。不知道如何處理。 –