2016-08-19 87 views
13

我需要检查所有定义是否包含一些特定数据。除了GroupBy返回空集合的情况外,它工作正常。Linq全部为空收集

var exist = dbContext.Definitions 
        .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
        .GroupBy(x => x.PropertyTypeId) 
        .All(...some condition...); 

如何重写这个所有将在空集合上返回false?

更新: 这是一个LINQ to SQL,我想在单个调用中执行此操作。

UPDATE2: 我想这样的作品:

var exist = dbContext.Definitions 
        .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
        .GroupBy(x => x.PropertyTypeId) 
        .Count(x => x 
         .All(...some condition...)) == propertyTypeIds.Count; 
+0

这是LINQ的对象或其他东西?答案可能完全不同。 –

+0

尝试使用AllOrDefault – elloco999

+1

'All'不会在空集合上返回false。看到这里:http://stackoverflow.com/questions/7884888/why-does-enumerable-all-return-true-for-an-empty-sequence – sr28

回答

0

如何编写自己的扩展方法是什么? (我敢肯定你会更好的名字吧)

public static bool NotEmptyAll<T>(
    this IEnumerable<T> collection, 
    Func<T, bool> predicate) 
{ 
    return collection != null 
     && collection.Any() 
     && collection.All(predicate); 
} 

然后调用它,而不是All

var exist = definitions.Where(
     x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
     .GroupBy(x => x.PropertyTypeId) 
     .NotEmptyAll(
      ...some condition...)); 
+5

请注意,这会执行查询两次,这可能是一个坏主意。 –

+0

感谢@JonSkeet的警告。但它是我想到的唯一解决方案。由于它没有标记'EntityFramework',我认为GENERALLY它不应该花费太多。 –

+0

那么它会花费两倍于做一次...它也是非常unLINQ,就像......没有常规的LINQ运算符不止一次地评估它的输入,IIRC。 –

1

嘛,你可以在两个步骤做:

var definitions = definitions.Where(
        x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
        .GroupBy(x => x.PropertyTypeId); 

var exist = definitions.Any() && definitions.All(...some condition...); 
+5

没有必要进行无效检查 - 但你需要知道这将执行两次查询。 –

+2

但是,如果这是类似于Entity Framework的东西,它将导致2个查询针对数据库运行。 – DavidG

+0

确实,它会执行两次查询。感谢您指出它。 – Fabjan

5

您可能能够使用Aggregate来做到这一点,沿线:

.Aggregate(new {exists = 0, matches = 0}, (a, g) => 
     new {exists = a.exists + 1, matches = a.matches + g > 10 ? 1 : 0}) 

(这里,g > 10是我的测试)

然后简单逻辑exists大于零并且existsmatches具有相同的值。

这可以避免运行整个查询两次。

3

您可以使用DefaultIfEmpty扩展方法,并调整您的some condition,以便它评估nullfalse

var exist = definitions 
    .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
    .GroupBy(x => x.PropertyTypeId) 
    .DefaultIfEmpty() 
    .All(...some condition...)); 
+1

非常聪明!换句话说,'.All(g => g!= null &&(...某些条件...))'。根据'...某些条件...'的写法,可能不需要额外的括号。 – devgeezer

1

编辑:第一个答案不会奏效。

如果你有点重新安排你的查询,你可以使用DefaultIfEmpty,而无需改变你的条件:

var exist = dbContext.Definitions 
        .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) 
            && x.CountryId == countryId) 
        .GroupBy(x => x.PropertyTypeId); 

      // apply the condition to all entries, 
      // resulting in sequence of bools (or empty), 
      // to permit the next step 
        .Select(...some condition...) 

      //if seq is empty, add `false` 
        .DefaultIfEmpty(false) 

      //All with identity function to apply the query and calculate result 
        .All(b => b) 
     ); 
+1

要从已删除的答案中窃取评论(您看不到):“但是,您需要否定Any的结果以获得与从All获得的结果相同的结果(如果全部为true,则返回true,否则返回false ),你回到了相同的情况“ – DavidG

+0

@DavidG该死的,没错。 – RoadieRich

+0

@DavidG是新版本更好吗? – RoadieRich

12

如果您使用LINQ到对象,我只是写我自己的扩展方法。我Edulinq project有示例代码为All,并适应这是非常简单的:

public static bool AnyAndAll<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate) 
{ 
    if (source == null) 
    { 
     throw new ArgumentNullException(nameof(source)); 
    } 
    if (predicate == null) 
    { 
     throw new ArgumentNullException(nameof(predicate)); 
    } 

    bool any = false; 
    foreach (TSource item in source) 
    { 
     any = true; 
     if (!predicate(item)) 
     { 
      return false; 
     } 
    } 
    return any; 
} 

这避免了评估一次输入多个。

0

这里是另一个特技:

var exist = dbContext.Definitions 
    .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
    .GroupBy(x => x.PropertyTypeId) 
    .Min(some_condition ? (int?)1 : 0) == 1; 

它利用这样的事实,上述Min<int?>方法返回:

(A)null当集为空
(B)0如果条件不满足某些元素
(C)1如果条件满足所有元素

所以我们使用可以为空的值比较规则来简单地检查(C)的结果。