2014-01-16 83 views
2

我有一個Linq表達式在幾個地方使用。我沿着表達式路線走,因爲沒有一種合理的方式來完成一些搜索邏輯,否則不會枚舉一個非常大的表。Linq All()/ Any()但不爲空

private Expression<Func<Property, bool>> PropertyIsCompliant() 
    { 
     return (p) => p.CalculationSets.OfType<SingleDocumentCalculationSet>() 
      .GroupBy(cs => cs.SourceDocument) 
      .Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults) 
      .SelectMany(cr => cr) 
      .All(cr => cr.Outcome == CalculationOutcome.Success); 
    } 

我的模型是這樣:

  • A樓盤有許多CalculationSets
  • 每個CalculationSet也被分配到一個文檔
  • 每個CalculationSet有許多CalculationResults的
  • 每個CalculationResult有一個結果

我試圖創建一個表達式,告訴我是否按最近(即最近的不同結果)排序的文檔分組的最新計算集的所有結果都是成功的。

我可以通過SelectMany子句從正確的CalculationSets中返回所有的CalculationResults。 我只是不知道如何返回true只有收集不是空的,他們都是結果。成功。

我明白所有的操作符會自動在空集合上返回true。我只是想不出一個辦法!

回答

0

我覺得你回答你的問題 - 如果你知道所有返回TRUE爲空然後你有兩個檢查。請原諒我的C#(我不知道對VAR的查詢任務,希望你的想法),但你可以做這樣的事情:

private Expression<Func<Property, bool>> PropertyIsCompliant() 
    { 
     var query = (p) => p.CalculationSets.OfType<SingleDocumentCalculationSet>() 
      .GroupBy(cs => cs.SourceDocument) 
      .Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults) 
      .SelectMany(cr => cr); 

     return (query.Count > 0) & query.All(cr => cr.Outcome == CalculationOutcome.Success); 
    } 
+1

這不是SQL翻譯,或者如果你強迫它將會是低效返回true。 – usr

+0

我使用了一個類似的解決方案(下圖),它可以正確轉換爲SQL。 –

1
var countsBySuccess = 
... 
.GroupBy(cr => cr.Outcome == CalculationOutcome.Success) //group on success 
.Select(g => new { IsSuccessful = g.Key, Count = g.Count() }); 

現在,您可以查看兩種結果行,以確保不成功的計數是零並且成功的計數是非零的。

關於性能,這將需要物化整個結果集服務器端並對其進行聚合。但它只有一次。

如果必須使用計算結果作爲一個更大的查詢的一部分,必須使用另一特技:

!countsBySuccess.Any(g => 
    g.IsSuccessful && Count == 0 || 
    !g.IsSuccessful && Count != 0) 

該布爾表達式確定你正在尋找的條件是否與所述數據的一個掃描成立。

重要的是隻掃描一次數據。不要乾脆寫:

myItems.All(cr => cr.Outcome == CalculationOutcome.Success) && myItems.Any() 

因爲做兩次掃描。 SQL Server不會優化它。

+0

我擔心的性能問題是將SQL DB中的所有記錄提取到應用程序。我不太關心它自己的SQL DB上的兩個獨立掃描。我已經用類似的方法解決了「不要這樣做」的建議! –

+0

這是一個很好的解決方案,如果你對兩次掃描都可以。 – usr

3

所以你的真實的條件是,有沒有任何不成功的結果。在這種情況下使用Any和反向條件:

   //V-- notice the ! inverse operator here 
    return (p) => !(p.CalculationSets.OfType<SingleDocumentCalculationSet>() 
     .GroupBy(cs => cs.SourceDocument) 
     .Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults) 
     .SelectMany(cr => cr) 
     .Any(cr => cr.Outcome != CalculationOutcome.Success)); 
+0

如果出現故障,這將返回true,儘管它應該返回false。 – usr

0

我不知道這是可能在表達式中使用「& &」。所以我設法結合了兩個單獨的表達式,給出我需要的答案。該「& &」只有當兩個表達式的計算「真」

  return (p) => 
      p.CalculationSets.OfType<SingleDocumentCalculationSet>() 
      .GroupBy(cs => cs.SourceDocument) 
      .Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults) 
      .SelectMany(cr => cr).Any() 
      && 
      p.CalculationSets.OfType<SingleDocumentCalculationSet>() 
      .GroupBy(cs => cs.SourceDocument) 
      .Select(g => g.OrderByDescending(d => d.DateTime).FirstOrDefault().CalculationResults) 
      .SelectMany(cr => cr) 
      .All(cr => cr.Outcome == CalculationOutcome.Success); 
+1

哇,這是很多重複。我更喜歡我的解決方案,即使我自己這樣說:) – usr

+0

它因爲第一個查詢只需要檢查是否存在一個CalculationSet而被更改。所以沒有分組,排序,需要選擇第一個查詢。 –

相關問題