Запрос EF LINQ с выражением: ожидается имя метода - Как передать выражение в функцию, которая будет использоваться в запросе EF? - PullRequest
0 голосов
/ 20 февраля 2019

У меня есть функция, которая должна возвращать различную информацию из запроса EF LINQ, основанного на выражении и / или лямбде, которые ему переданы.

Вот мой код:

public static ObservableCollection<SI> GetDisplayStratsByJurisd(string jurisd, short Year, 
      Expression<Func<string, bool>> lambStat)
    {
        var ctx = new MI_Entities(server, database);

        var strats = from y in ctx.SIset.AsNoTracking()
                     where y.Jurisd == jurisd && y.Year_ID == Year  && lambStat(y.Status)
                     select y;
        return new ObservableCollection<SI>(strats);
    }

Компилятор выдает мне следующую ошибку:

Ожидается имя метода

Если я использую это вместо:

    public static ObservableCollection<SI> GetDisplayStratsByJurisd(string jurisd, short Year, 
      Expression<Func<string, bool>> lambStat)
    {
        var ctx = new MI_Entities(server, database);
        Func<string, bool> bob = lambStat.Compile();
        var strats = from y in ctx.SIset.AsNoTracking()
                     where y.Jurisd == jurisd && y.Year_ID == Year  && bob(y.Status)
                     select y;
        return new ObservableCollection<SI>(strats);
    }

Тогда я получаю другую ошибку:

Тип узла выражения LINQ 'Invoke' не поддерживается в LINQ to Entities

Итак, я не уверен, как перейти к передаче лямбда-выражения вфункция, так что он может быть использован в запросе.Можно ли это сделать?Если да, то как?

1 Ответ

0 голосов
/ 21 февраля 2019

Итак, у вас есть Expression<Func<TSource, bool>>, и вы хотите объединить их в один Expression<Func<TSource, bool>>, используя функцию AND, чтобы вы могли использовать ее в качестве AsQueryable в фреймворке сущностей.

Было бы неплохо иметь этов стиле LINQ, поэтому мы можем поместить его между конкатенацией операторов Linq.

Давайте создадим некоторые функции расширения.

// A function that takes two Expression<Func<TSource, bool>> and returns the AND expression
static Expression<Func<TSource, bool>> AndAlso<TSource> (
    this Expression<Func<TSource, bool>> x,
    Expression<Func<TSource, bool>> y)
{
     // TODO implement
}

Использование:

 Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
 Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
 Expression<Func<Student, bool>> exprAND = expr1.AndAlso(expr2);

 var brummyMaleStudents = dbContext.Students.Where(exprAnd).Select(...);

Давайте реализуем AndAlso

Обычно A будет выглядеть следующим образом:

.Where(student  => student.City == "Birmingham" && student.Gender == Gender.Male)

Входной параметр student используется в качестве ввода для левого выражения и в качестве ввода для правого выражения.Нам нужно что-то, что говорит:

Возьмите один входной параметр типа Student, поместите его в левое выражение и в правое выражение и выполните AND между двумя логическими возвращаемыми значениями.Вернуть логический результат.

Для этого мы создаем класс, производный от System.Linq.Expressions.ExpressionVisitor.

Этот класс представляет действие: «поместите ученика в выражение и вычислите его».Этот расчет называется «посещение выражения».Ввод выражения - это выражение, результат посещения - другое выражение:

internal class ReplaceExpressionVisitor : ExpressionVisitor
{
    private readonly Expression oldValue;
    private readonly Expression newValue;

    public ReplaceExpressionVisitor(ParameterExpression oldValue,
                                    ParameterExpression newValue)
    {
        this.oldValue = oldValue;
        this.newValue = newValue;
    }

    public override Expression Visit(Expression node)
    {
        if (node == this.oldValue)
        {   // "my" expression is visited
            return this.newValue;
        }
        else
        {   // not my Expression, I don't know how to Visit it, let the base class handle this
            return base.Visit(node);
        }
    }
}

Теперь, когда мы создали посетитель выражения, мы можем реализовать AndAlso:

static Expression<Func<TSource, bool>> AndAlso<TSource>(
    this Expression<Func<TSource, bool>> expr1,
         Expression<Func<TSource, bool>> expr2)
{
     // Create one expression that represent expr1 && expr2
     // the input of expr1 is a TSource,
     // the input of expr2 is a TSource,
     // so the input of expr1 && expr2 is a TSource:
     ParameterExpression inputParameter = Expression.Parameter(typeof(TSource));

     // Visit the left part of the AND:
     var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], inputParameter)
     var left = leftVisitor.Visit(expr1.Body);

     // Visit the right part of the AND:
     var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
     var right = rightVisitor.Visit(expr2.Body);

     // Combine left and right with a binary expression representing left && right:
     var andExpression = Expression.AndAlso(left, right);

     // return the lambda expression that takes one Tsource as input and returns the AND:
     var lambda = Expression.Lambda<Func<TSource, bool>>(andExpression, new[] {parameter});
     return lambda;
}

Использование:

 Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
 Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;

 var brummyMaleStudents = dbContext.Students.Where(expr1.AndAlso(expr2));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...