Linq All on empty collection

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

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

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

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

更新2:我认为这有效:

 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; 

如果你正在使用LINQ to Objects,我只想编写自己的扩展方法。 我的Edulinq项目有All示例代码,并且调整非常简单:

 public static bool AnyAndAll( this IEnumerable source, Func 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; } 

这避免了多次评估输入。

您可以使用Aggregate执行此操作, 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具有相同的值。

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

您可以使用DefaultIfEmpty扩展方法,并调整您的some condition以便将null计算为false

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

那么,你可以分两步完成:

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

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

如果您稍微重新排列查询,则可以使用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) ); 

这是另一个技巧:

 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方法返回的事实:

(A)当集合为空时为null
(B)如果某个元素不满足条件,则为0
(C)如果满足所有要素的条件,则为1

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

那么编写自己的扩展方法呢? (我很确定你会更好地命名)

 public static bool NotEmptyAll( this IEnumerable collection, Func 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...));