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