2014-01-16 45 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而被更改。所以没有分组,排序,需要选择第一个查询。 –

相关问题