Как использовать предикаты в 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