Как использовать предикаты в LINQ to Entities для объектов Entity Framework
Я использую LINQ для сущностей для объектов Entity Framework в моем слое доступа к данным.
моя цель-фильтровать как можно больше из базы данных, не применяя логику фильтрации к результатам в памяти.
для этой цели уровень бизнес-логики передает предикат на уровень доступа к данным.
Я имею в виду
Func<MyEntity, bool>
Итак, если я использую этот предикат напрямую, например
public IQueryable<MyEntity> GetAllMatchedEntities(Func<MyEntity, Boolean> isMatched)
{
return qry = _Context.MyEntities.Where(x => isMatched(x));
}
Я получаю исключение
[системы.NotSupportedException] - - - {"тип узла выражения LINQ "Invoke" не поддерживается в LINQ to Entities."}
решение этот вопрос предлагает использовать метод AsExpandable () из LINQKit библиотека.
но, опять же, используя
public IQueryable<MyEntity> GetAllMatchedEntities(Func<MyEntity, Boolean> isMatched)
{
return qry = _Context.MyEntities.AsExpandable().Where(x => isMatched(x));
}
Я получаю исключение
невозможно привести объект типа - Система.В LINQ.Выражения.FieldExpression на тип - Система.В LINQ.Выражения.Лямбда-выражение'
есть ли способ использовать предикат в LINQ to Entities query для объектов Entity Framework, чтобы правильно преобразовать его в инструкцию SQL.
спасибо.
2 ответов
вам не нужен LinqKit для этого. Просто не забудьте использовать
Expression<Func<MyEntity, bool>>
вместо
Func<MyEntity, bool>
что-то вроде этого:
public IQueryable<MyEntity> GetAllMatchedEntities(Expression<Func<MyEntity, Boolean>> predicate)
{
return _Context.MyEntities.Where(predicate);
}
вы должны использовать выражение, потому что Linq to Entities должен перевести вашу лямбду на SQL.
когда вы используете Func, лямбда компилируется в IL, но при использовании Expression это дерево выражений, которое Linq to Entities может поперечно и конвертировать.
это работает с выражениями, которые Linq to Сущности понимают.
если он продолжает терпеть неудачу, то ваше выражение делает то, что Linq для сущностей не может перевести на SQL. В этом случае я не думаю, что LinqKit поможет.
Edit:
преобразование не требуется. Просто определите метод GetAllMatchedEntities с параметром Expression и используйте его так же, как и с параметром Func. Компилятор делает все остальное.
есть три способа использования GetAllMatchedEntities.
1) с встроенным лямбда-выражением:
this.GetAllMatchedEntities(x => x.Age > 18)
2) Определите свое выражение как поле (также может быть переменной)
private readonly Expression<Func<MyEntity, bool>> IsMatch = x => x.Age > 18;
...then use it
this.GetAllMatchedEntities(IsMatch)
3) Вы можете создать выражение вручную. Уменьшение размера-это больше кода, и вы пропускаете проверки времени компиляции.
public Expression<Func<MyEntity, bool>> IsMatchedExpression()
{
var parameterExpression = Expression.Parameter(typeof (MyEntity));
var propertyOrField = Expression.PropertyOrField(parameterExpression, "Age");
var binaryExpression = Expression.GreaterThan(propertyOrField, Expression.Constant(18));
return Expression.Lambda<Func<MyEntity, bool>>(binaryExpression, parameterExpression);
}
методы, используемые в Linq для сущностей, должны быть канонически сопоставлены поставщиком Linq для работы. Поскольку поставщик Linq, EF в вашем случае, не смог сопоставить ваш предикат с внутренним методом, он выдал ошибку.
для сценариев LINQ запросы к Entity Framework включают сопоставление некоторых методов CLR методов в базовом источнике данных через канонические функции. Любой метод вызывает в LINQ сущности запрос, не являющийся явным подключенный к канонической функции в результате во время выполнения NotSupportedException создается исключение
источник: метод CLR для отображения канонических функций (http://msdn.microsoft.com/en-us/library/bb738681.aspx)
можно попробовать взять те методы, которые ARE сопоставить и связать их в выражение LINQ, или использовать хранимую процедуру. Но пока EF не поддерживает все CLR, вам придется найти обходной.
с положительной стороны, каждый выпуск, кажется, добавляет немного больше в канонический список.
стоит почитать, как можно обойти: http://msdn.microsoft.com/en-us/library/dd456857.aspx