2013-08-30 111 views
3

我們正在嘗試優化我們的一些方法。我們使用Redgate的性能分析器來查找一些性能泄漏。c#Linq to Objects - FirstOrDefault performance

我們的工具在幾種方法中使用Linq對象。但我們注意到FirstOrDefault在+/- 1000個對象的集合上需要很長的時間。

分析器還提示查詢速度很慢。我已經使用分析器結果添加了圖像。

無法將集合添加到數據庫,然後查詢數據庫。 有什麼建議嗎?

謝謝!

private SaldoPrivatiefKlantVerdeelsleutel GetParentSaldoPrivatiefKlantVerdeelsleutel(SaldoPrivatiefKlantVerdeelsleutel saldoPrivatiefKlantVerdeelsleutel, SaldoGebouwRekeningBoeking boeking, int privatiefKlant) 
{ 
    SaldoPrivatiefKlantVerdeelsleutel parentSaldoPrivatiefKlantVerdeelsleutel = null; 

    if (saldoPrivatiefKlantVerdeelsleutel != null) 
    { 
     try 
     { 
      parentSaldoPrivatiefKlantVerdeelsleutel = saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection 
       .FirstOrDefault(s => (boeking == null || (s.SaldoVerdeelsleutel != null && 
       (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) 
       && s.PrivatiefKlant.ID == privatiefKlant); 
     } 
     catch (Exception ex) 
     { } 
    } 

    return parentSaldoPrivatiefKlantVerdeelsleutel; 
} 

圖片: Profile report

+0

我首先想到的是移動'的預訂== null'檢查和'SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID'值_outside_你的循環。沒有理由爲您的收藏中的每個元素重新評估它。 –

+1

從表達式讀取時執行查詢。在這種情況下,FirstOrDefault將執行整個表達式樹,所以它不是FirstOrDefault,它很慢。 –

+2

必填註釋:一個空的'catch {}'是非常有害的,請將其刪除。 –

回答

6

您應該能夠通過改寫它作爲

saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection 
      .Where(s => (boeking == null || (s.SaldoVerdeelsleutel != null && 
       (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) && s.PrivatiefKlant.ID == privatiefKlant) 
      .FirstOrDefault() 

Why is LINQ .Where(predicate).First() faster than .First(predicate)?爲什麼這是更快地加速這一過程。

+0

有趣。 +1 – stevepkr84

2

FirstOrDefault對源集合執行標準線性搜索並返回與謂詞相匹配的第一個元素。這是O(n),所以毫不奇怪,它需要更多的時間在更大的收藏。

您可以嘗試以下操作,但收益不會很大,因爲它仍然是O(n)

private SaldoPrivatiefKlantVerdeelsleutel GetParentSaldoPrivatiefKlantVerdeelsleutel(SaldoPrivatiefKlantVerdeelsleutel saldoPrivatiefKlantVerdeelsleutel, SaldoGebouwRekeningBoeking boeking, int privatiefKlant) 
{ 
    SaldoPrivatiefKlantVerdeelsleutel parentSaldoPrivatiefKlantVerdeelsleutel = null; 

    if (saldoPrivatiefKlantVerdeelsleutel != null) 
    { 
     try 
     { 
      var query = saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode 
                 .SaldoPrivatiefKlantVerdeelsleutelCollection 
                 .Where(s => s.PrivatiefKlant.ID == privatiefKlant); 

      if(boeking != null) 
      { 
       var gebouwVerdeelSleutelId = boeking.SaldoGebouwRekeningVerdeling 
                .SaldoGebouwRekening 
                .SaldoVerdeelsleutel 
                .GebouwVerdeelSleutel 
                .ID; 

       query = query.Where(s => s.SaldoVerdeelsleutel != null && 
        s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == gebouwVerdeelSleutelId); 
      } 
      parentSaldoPrivatiefKlantVerdeelsleutel = query.FirstOrDefault(); 
     } 
     catch (Exception ex) 
     { } 
    } 

    return parentSaldoPrivatiefKlantVerdeelsleutel; 
} 

它會做出更好的,因爲boeking != null支票將只進行一次,不是每個源集合元素。而且由於nested Where calls are combined他們不會造成性能損失。

2

我會說,這

boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID 

可能是罪魁禍首。嘗試在外部緩存它,如:

var id = boeking != null ? boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID : 0; 

並在查詢中使用id

(我做一個假設:即長鏈的特性之一做什麼「沒有太多聰明」實際上是在相當慢)

0

,您可以嘗試寫這樣一個簡單的代碼。 LINQ使用代表這就是爲什麼有一點點性能打擊。

   try 
       { 
        parentSaldoPrivatiefKlantVerdeelsleutel = null; 
        foreach (var s in saldoPrivatiefKlantVerdeelsleutel.AfrekenPeriode.SaldoPrivatiefKlantVerdeelsleutelCollection) 
        { 
         if ((boeking == null || (s.SaldoVerdeelsleutel != null && (s.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID == boeking.SaldoGebouwRekeningVerdeling.SaldoGebouwRekening.SaldoVerdeelsleutel.GebouwVerdeelSleutel.ID))) && s.PrivatiefKlant.ID == privatiefKlant) 
         { 
          parentSaldoPrivatiefKlantVerdeelsleutel = s; 
          break; 
         } 
        } 
       } 
       catch (Exception ex) 
       { }