Подтвердить что ты не робот

Linq Все в пустой коллекции

Мне нужно проверить, содержат ли все определения некоторые конкретные данные. Он отлично работает, за исключением случая, когда GroupBy возвращает пустую коллекцию.

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

Как переписать это, чтобы All вернул false в пустую коллекцию?

UPDATE: Это 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;
4b9b3361

Ответ 1

Если вы используете LINQ для объектов, я просто напишу свой собственный метод расширения. Мой проект Edulinq имеет образец кода для 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;
}

Это позволяет избежать оценки ввода более одного раза.

Ответ 2

Вы можете сделать это, используя Aggregate, по строкам:

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

(Здесь g > 10 - мой тест)

И тогда простая логика, что exists больше нуля и что exists и matches имеют одинаковое значение.

Это позволяет избежать выполнения всего запроса дважды.

Ответ 3

Вы можете использовать метод расширения 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...));

Ответ 4

Ну, вы можете сделать это в два этапа:

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

Изменить: первый ответ не сработал.

Если вы немного измените свой запрос, вы можете использовать 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)
         );

Ответ 6

Как насчет написания собственного метода расширения? (Я уверен, вы назовете это лучше)

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

Вот еще один трюк:

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), используя правила сравнения значений с нулевым значением.