Как использовать предикаты в объектах LINQ to Entities for Entity Framework
Я использую LINQ для объектов Entities для Entity Framework на моем уровне доступа к данным.
Моя цель - отфильтровать как можно больше из базы данных, не применяя логику фильтрации к результатам в памяти.
Для этого Business Logic Layer передает предикат уровню доступа к данным.
Я имею в виду
Func<MyEntity, bool>
Итак, если я использую этот предикат напрямую, например
public IQueryable<MyEntity> GetAllMatchedEntities(Func<MyEntity, Boolean> isMatched)
{
return qry = _Context.MyEntities.Where(x => isMatched(x));
}
Я получаю исключение
[System.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));
}
Я получаю исключение
Невозможно передать объект типа "System.Linq.Expressions.FieldExpression" для ввода "System.Linq.Expressions.LambdaExpression"
Есть ли способ использовать предикат в запросе LINQ to Entities для объектов Entity Framework, чтобы он правильно преобразовал его в инструкцию SQL.
Спасибо.
Ответы
Ответ 1
Для этого вам не нужен 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 необходимо перевести вашу lambda в SQL.
Когда вы используете Func, ваш лямбда компилируется в IL, но при использовании Expression это дерево выражений, которое Linq to Entities может передавать и преобразовывать.
Это работает с выражениями, понимаемыми Linq to Entities.
Если он продолжает терпеть неудачу, ваше выражение делает то, что Linq to Entities не может перевести на SQL. В этом случае я не думаю, что LinqKit поможет.
Редактировать:
Не требуется никакого преобразования. Просто определите метод 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);
}
Ответ 2
Методы, используемые в Linq to Entities, должны быть канонически отображены поставщиком Linq для работы. Поскольку поставщик Linq, EF в вашем случае, не смог сопоставить ваш предикат с внутренним методом, он выдал ошибку.
Для сценариев LINQ запросы к платформе Entity Framework включают сопоставление некоторых методов CLR с методами на базовом источнике данных с помощью канонических функций. Любые вызовы методов в запросе LINQ to Entities, которые явно не отображаются в каноническую функцию, приведут к тому, что исключение исключений NotSupportedException
Источник: метод CLR для канонического отображения функций (http://msdn.microsoft.com/en-us/library/bb738681.aspx)
Вы можете попытаться использовать те методы, которые ARE сопоставлены, и связать их с выражением Linq или использовать хранимую процедуру. Но пока EF не поддерживает все CLR, вам останется найти работу.
С положительной стороны, каждый релиз, кажется, добавляет немного больше в канонический список.
Стоит читать как возможный рабочий процесс: http://msdn.microsoft.com/en-us/library/dd456857.aspx