#c# #.net-core #entity-framework-core #linq-to-entities
#c# #.net-ядро #сущность-фреймворк-ядро #linq-to-entities
Вопрос:
у меня есть объект с именем schoolrep, и этот schoolrep содержит много идентификаторов уровня образования, мой запрос linq выдает ошибку
Указанные критерии фильтрации были недействительными
я отправлю список идентификаторов уровня образования, и мне нужно вернуть schoolrep, у которого есть все уровни образования, отправленные в фильтр
ниже приведен фильтр, я пытаюсь сделать его фиксированными значениями для тестирования
List<long> lst = new List<long> { 1, 2, 3 };
ниже приведено выражение
Expression <Func<SchoolRepresentative, bool>> filterExpression = x =>
x.SchoolRepEducationLevels.Select(x=>x.EducationLevelId).SequenceEqual(lst)
Комментарии:
1. Короткий ответ — это не поддерживается.
2. как я могу справиться с этой проблемой
3. На концептуальном уровне поймите, что написать SQL для этого сложно. Затем, как только вы примете это, поймите, что именно поэтому он не поддерживается. 🙂
4. На практике это означает, что вам, скорее всего, нужно будет сделать это в памяти, а не в БД (т. Е. Вытащить все это, затем выполнить SequenceEqual локально).
5. schoolrep, у которого есть все уровни образования, отправленные в filter, но
SequnceEquals
этого не делает, он делает больше.
Ответ №1:
Это означает, что EF не поддерживает SequenceEquals
метод при переводе вашего запроса в SQL.
Я не знаю, существует ли эквивалентный компактный SQL-запрос (найдите все объекты, где его единственные дочерние объекты имеют идентификаторы 1, 2 и 3).
Одним из вариантов было бы загрузить все объекты, у которых есть дочерний элемент 1, 2 или 3, используя x => lst.Contains(x.EducationLevelId)
в качестве фильтра, а затем проверить наличие всех уровней в памяти, используя любой метод, который вы хотите.
Ответ №2:
Используя LinqKit, вы можете создать метод расширения, который будет транслировать в SQL тестовое выражение.
public static class IQueryableExt { // using LINQKit
// searchTerms - IEnumerable<TSearch> where all must match for a row
// testFne(row,searchTerm) - test one of searchTerms against a row
// r => searchTerms.All(s => testFne(r,s))
public static Expression<Func<T, bool>> AllAre<T, TSearch>(this IEnumerable<TSearch> searchTerms, Expression<Func<T, TSearch, bool>> testFne) {
var pred = PredicateBuilder.New<T>();
foreach (var s in searchTerms)
pred = pred.And(r => testFne.Invoke(r, s));
return (Expression<Func<T, bool>>)pred.Expand();
}
// searchTerms - IEnumerable<TSearch> where one must match for a row
// testFne(row,searchTerm) - test one of searchTerms against a row
// r => searchTerms.All(s => testFne(r,s))
public static Expression<Func<T, bool>> AnyIs<T, TSearch>(this IEnumerable<TSearch> searchTerms, Expression<Func<T, TSearch, bool>> testFne) {
var pred = PredicateBuilder.New<T>();
foreach (var s in searchTerms)
pred = pred.Or(r => testFne.Invoke(r, s));
return (Expression<Func<T, bool>>)pred.Expand();
}
}
Предполагая, что вам не нужно, SequenceEquals
но только то, что все lst
включено, теперь вы можете создать выражение фильтра, используя AllAre
:
var filterExpression = lst.AllAre((SchoolRepresentative sr, long l) => sr.SchoolRepEducationLevels.Select(srel => srel.EducationLevelId).Contains(l));