2012-02-29 101 views
0

我從Linq查詢返回一個列表,之後我必須用for循環填充它中的值。 問題是它太慢了。查詢和foreach的Linq優化

var formentries = (from f in db.bNetFormEntries 
      join s in db.bNetFormStatus on f.StatusID.Value equals s.StatusID into entryStatus 
      join s2 in db.bNetFormStatus on f.ExternalStatusID.Value equals s2.StatusID into entryStatus2 
      where f.FormID == formID 
      orderby f.FormEntryID descending 
      select new FormEntry 
      { 
       FormEntryID = f.FormEntryID, 
       FormID = f.FormID, 
       IPAddress = f.IpAddress, 
       UserAgent = f.UserAgent, 
       CreatedBy = f.CreatedBy, 
       CreatedDate = f.CreatedDate, 
       UpdatedBy = f.UpdatedBy, 
       UpdatedDate = f.UpdatedDate, 
       StatusID = f.StatusID, 
       StatusText = entryStatus.FirstOrDefault().Status, 
       ExternalStatusID = f.ExternalStatusID, 
       ExternalStatusText = entryStatus2.FirstOrDefault().Status 
      }).ToList(); 

,然後我以這種方式使用的:

for(var x=0; x<formentries.Count(); x++) 
{ 
    var values = (from e in entryvalues 
       where e.FormEntryID.Equals(formentries.ElementAt(x).FormEntryID) 
       select e).ToList<FormEntryValue>(); 
    formentries.ElementAt(x).Values = values; 
} 
return formentries.ToDictionary(entry => entry.FormEntryID, entry => entry); 

但絕對是太慢了。 有沒有辦法讓它更快?

+2

每次調用formentries.Count和formentries.ElementAt都會再次執行基礎查詢。你應該緩存查詢結果,並通過在Formentries上調用ToArray或ToList(在保存結果之前)對其進行操作,然後再通過Formentries執行你的邏輯 – Polity 2012-02-29 15:06:07

+1

@Polity它已經物化的查詢 – 2012-02-29 15:13:30

+0

請不要在「C# 「等等。這就是標籤的用途。 – 2012-02-29 15:13:39

回答

0

對於我所看到的,您在沒有任何理由的情況下多次遍歷formentries - 當您填充值時,以及何時轉換爲字典。

如果entryvalues是一個數據庫驅動的 - 即你從數據庫中獲取它們,然後把值田間種羣在第一個查詢。

如果不是的話,那麼你不需要調用ToList()在第一個查詢,執行循環,然後字典建立。

var formentries = from f in db.bNetFormEntries 
       join s in db.bNetFormStatus on f.StatusID.Value equals s.StatusID into entryStatus 
       join s2 in db.bNetFormStatus on f.ExternalStatusID.Value equals s2.StatusID into entryStatus2 
       where f.FormID == formID 
       orderby f.FormEntryID descending 
       select new FormEntry 
       { 
        FormEntryID = f.FormEntryID, 
        FormID = f.FormID, 
        IPAddress = f.IpAddress, 
        UserAgent = f.UserAgent, 
        CreatedBy = f.CreatedBy, 
        CreatedDate = f.CreatedDate, 
        UpdatedBy = f.UpdatedBy, 
        UpdatedDate = f.UpdatedDate, 
        StatusID = f.StatusID, 
        StatusText = entryStatus.FirstOrDefault().Status, 
        ExternalStatusID = f.ExternalStatusID, 
        ExternalStatusText = entryStatus2.FirstOrDefault().Status 
       }; 

var formEntryDictionary = new Dictionary<int, FormEntry>(); 

foreach (formEntry in formentries) 
{ 
    formentry.Values = GetValuesForFormEntry(formentry, entryvalues); 
    formEntryDict.Add(formEntry.FormEntryID, formEntry); 
} 

return formEntryDictionary; 

,值準備:

private IList<FormEntryValue> GetValuesForFormEntry(FormEntry formEntry, IEnumerable<FormEntryValue> entryValues) 
{ 
    return (from e in entryValues 
        where e.FormEntryID.Equals(formEntry.FormEntryID) 
        select e).ToList<FormEntryValue>(); 
} 

您可以更改私有方法只能接受,如果你想ENTRYID而不是整個formEntry。

7

這肯定太慢了。有沒有辦法讓它更快?

也許吧。也許不會。但這不是一個正確的問題。正確的問題是:

爲什麼這麼慢?

如果你有第二個問題的答案,找出第一個問題的答案要容易得多!如果第二個問題的答案是「因爲數據庫在東京,而我在羅馬,並且數據包移動速度不比光速快是造成我無法接受的放緩的原因」,那麼您的方式更快是你搬到日本;固定查詢的數量不會改變光的速度。

要找出爲什麼這麼慢,得到一個分析器。通過分析器運行代碼並使用它來確定您花費大部分時間在哪裏。然後看看你是否可以加快這一部分。

0

這是緩慢的,因爲你的O(N*M)其中Nformentries.CountMentryvalues.Count即使是一個簡單的測試,我越來越慢20倍以上,只有1000元的任何我喜歡的類型只有一個int id場,與列表中的10000元也比下面的代碼慢了1600多倍!

假設你entryvalues的本地列表,而不是打一個數據庫(只是它.ToList()到一個新的變量,如果某處的話),並假設你FormEntryId是唯一的(這似乎是從.ToDictionary通話那就試試這個而不是:

var entryvaluesDictionary = entryvalues.ToDictionary(entry => entry.FormEntryID, entry => entry); 
for(var x=0; x<formentries.Count; x++) 
{ 
    formentries[x] = entryvaluesDictionary[formentries[x].FormEntryID]; 
} 
return formentries.ToDictionary(entry => entry.FormEntryID, entry => entry); 

應該很長的路要走,以使得它至少變得更好

變化:的.Count代替.Count()只是因爲它是最好不要調用擴展方法時,你不需要。使用字典查找val而不是通過for循環中的每個x值來有效地從bigO中刪除M

如果這不完全正確,我相信你可以改變缺失的東西來適應你的工作案例。但是,順便說一句,你應該真的考慮使用你的變量名稱formentriesformEntries,只是這樣一點點容易閱讀。

0

對於您使用formentries的方式,這可能會有些緩慢。

  • 的formentries List<T>從上面有一個Count屬性,但您呼叫的枚舉Count()擴展方法來代替。此擴展可能會或可能不會有一個優化,可以檢測到您正在使用具有可遵循的Count屬性的集合類型,而不是漫步枚舉來計算計數。
  • 類似地,formEntries.ElementAt(x)表達式被使用兩次;如果他們沒有優化ElementAt以確定他們正在處理像列表這樣的集合,那麼LINQ將不得不冗餘地遍歷列表以獲得第x個項目。

上述評估可能會漏掉真正的問題,如果您的配置文件只是您真正知道的問題。然而,就可以避免以上,同時使你的代碼顯著更容易,如果你切換你如何迭代的formentries收集如下閱讀:

foreach(var fe in formentries) 
{ 
    fe.Values = entryvalues 
     .Where(e => e.FormEntryID.Equals(fe.FormEntryID)) 
     .ToList<FormEntryValue>(); 
} 
return formentries.ToDictionary(entry => entry.FormEntryID, entry => entry); 

你可能已經使出了for(var x=...) ...ElementAt(x)的方法,因爲你以爲你不能修改由foreach循環變量fe引用的對象上的屬性。

也就是說,另一點可能是一個問題是如果formentries有多個項目相同FormEntryID。這會導致循環內多次執行相同的工作。雖然頂級查詢看起來是針對數據庫的,但您仍然可以使用linq-to-object land中的數據進行連接。快樂的優化/分析/編碼 - 讓我們知道什麼適合您。