Linq All on empty collection

时间:2016-08-19 12:39:38

标签: c# .net entity-framework linq

我需要检查所有定义是否包含某些特定数据。除了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,我想在单个调用中执行它。

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;

8 个答案:

答案 0 :(得分:12)

如果你正在使用LINQ to Objects,我只会编写自己的扩展方法。我的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;
}

这可以避免多次评估输入。

答案 1 :(得分: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具有相同值的简单逻辑。

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

答案 2 :(得分:4)

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

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

答案 3 :(得分:1)

嗯,你可以分两步完成:

$ cap production ssh:doctor --trace
** Invoke production (first_time)
** Execute production
** Invoke load:defaults (first_time)
** Execute load:defaults
** Invoke bundler:map_bins (first_time)
** Execute bundler:map_bins
** Invoke deploy:set_rails_env (first_time)
** Execute deploy:set_rails_env
** Invoke deploy:set_linked_dirs (first_time)
** Execute deploy:set_linked_dirs
** Invoke deploy:set_rails_env 
** Invoke rbenv:validate (first_time)
** Execute rbenv:validate
cap aborted!
NoMethodError: undefined method `on' for main:Object
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/capistrano-rbenv-2.0.4/lib/capistrano/tasks/rbenv.rake:3:in `block (2 levels) in <top (required)>'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:248:in `block in execute'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:243:in `each'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:243:in `execute'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:187:in `block in invoke_with_call_chain'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/2.3.0/monitor.rb:214:in `mon_synchronize'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:180:in `invoke_with_call_chain'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:173:in `invoke'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/capistrano-3.2.1/lib/capistrano/dsl/task_enhancements.rb:12:in `block in after'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:248:in `block in execute'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:243:in `each'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:243:in `execute'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:187:in `block in invoke_with_call_chain'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/2.3.0/monitor.rb:214:in `mon_synchronize'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:180:in `invoke_with_call_chain'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/task.rb:173:in `invoke'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:152:in `invoke_task'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:108:in `block (2 levels) in top_level'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:108:in `each'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:108:in `block in top_level'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:117:in `run_with_threads'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:102:in `top_level'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:80:in `block in run'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:178:in `standard_exception_handling'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/rake-11.2.2/lib/rake/application.rb:77:in `run'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/capistrano-3.2.1/lib/capistrano/application.rb:15:in `run'
/Users/admin/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/capistrano-3.2.1/bin/cap:3:in `<top (required)>'
/Users/admin/.rbenv/versions/2.3.1/bin/cap:23:in `load'
/Users/admin/.rbenv/versions/2.3.1/bin/cap:23:in `<main>'
Tasks: TOP => rbenv:validate

答案 4 :(得分: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)
         );

答案 5 :(得分:1)

这是另一个技巧:

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)的结果。

答案 6 :(得分: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...));

答案 7 :(得分:0)

这是All的替代方法,如果集合为空,则返回false

var collection = Enumerable.Range(0, 0); //empty collection

collection
  .Select(IsValid)
  .DefaultIfEmpty(false)
  .All(b => b);

或作为扩展方法:

public static bool AnyAndAll<T>(IEnumerable<T> collection, Func<T, bool> predicate) =>
  collection
    .Select(predicate)
    .DefaultIfEmpty(false)
    .All(b => b);