Цепочка лямбда-выражений точкой - PullRequest
0 голосов
/ 25 февраля 2019

У меня есть два выражения, и я хочу объединить их в цепочку, чтобы полученное выражение содержало оба входных выражения.

Expression<Func<IQueryable<Material>, object>> expression1 = x => x.Include(m => m.MaterialGroup);
Expression<Func<IQueryable<Material>, object>> expression2 = x => x.Include(m => m.MaterialSomething);

var expression3 = expression1.Update(expression2.Body, expression2.Parameters);

Сейчас expression3 содержит только x => x.Include(m => m.MaterialSomething), поэтому оно переопределяет второе выражение.Мне бы хотелось, чтобы оно было x => x.Include(m => m.MaterialGroup).Include(m => m.MaterialSomething).

. Я намерен добиться программного объединения нескольких выражений включения, чтобы иметь возможность построить более эффективную систему для быстрой загрузки в EF Core.

РЕДАКТИРОВАТЬ: Это не вопрос ANDing, ORing и т. Д., Потому что я хочу, чтобы эти выражения были связаны (как цепочка точек), а не логически соединены.

Daniel

Ответы [ 2 ]

0 голосов
/ 10 июня 2019

Я хотел бы добавить еще один способ архивации цепочек лямбда-выражений:

добавить следующий статический метод где-нибудь легкий доступ

public static Expression<Func<T, bool>> ConcatLambdaExpression<T>(Expression<Func<T, bool>> firstExpression, Expression<Func<T, bool>> secondExpression)
{
    var invokedThird = Expression.Invoke(secondExpression, firstExpression.Parameters.Cast<Expression>());
    var finalExpression = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(firstExpression.Body, invokedThird), firstExpression.Parameters);
    return finalExpression;
}

Тогда вы можете использовать его следующим образом:

public PersonDTO GetAll()
{
    Expression<Func<Person, bool>> expression = x => x != null;
    expression = x => x.Name == "John";

    Expression<Func<Person, bool>> pred = x => x.LastName == "Doe" || x.LastName == "Wick";

    //result of expression would be:  
    ////expression = x.Name == "John" && (x => x.LastName == "Doe" || x.LastName == "Wick")

    expression = Utilities.ConcatLambdaExpression(expression, pred);

    var result = Context.PersonEntity.Where(expression);

    //your code mapping results to PersonDTO
    ///resultMap...            

    return resultMap;
}
0 голосов
/ 25 февраля 2019

Поскольку Include является методом расширения, ваше выражение

x => x.Include(m => m.MaterialGroup);

на самом деле является

x => QueryableExtensions.Include(x, m => m.MaterialGroup);

Так что для объединения ваших выражений необходимо заменить первый аргумент Include на вызовеще один Include

x => QueryableExtensions.Include(
  QueryableExtensions.Include(x, m => m.MaterialSomething),
  m => m.MaterialGroup);

Следующий код сделает эту цепочку

public static Expression<Func<IQueryable<T>, object>> Chain<T>(
  params Expression<Func<IQueryable<T>, object>>[] expressions)
{
    if (expressions.Length == 0)
        throw new ArgumentException("Nothing to chain");

    if (expressions.Length == 1)
        return expressions[0];

    Expression body = expressions[0].Body;
    var parameter = expressions[0].Parameters[0];
    foreach (var expression in expressions.Skip(1))
    {
        var methodCall = (MethodCallExpression)expression.Body;
        var lambda = (UnaryExpression)methodCall.Arguments[1];

        body = Expression.Call(typeof(QueryableExtensions),
            "Include",
            new []{ typeof(T), ((LambdaExpression)lambda.Operand).Body.Type},
            body, lambda
            );
    }

    return Expression.Lambda<Func<IQueryable<T>, object>>(body, parameter);
}

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

var expression = Chain(expression1, expression2 /*, expression3 .... */);

Вы можете проверить это онлайн здесь

Обратите внимание, что этот код пропускает проверку выражения для краткости.

...