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