Подтвердить что ты не робот

Как работает PredicateBuilder

С# в Nutshell имеет свободный класс PredicateBuilder, который конструирует предикаты LINQ по частям, доступные здесь. Здесь выдержка из метода, который добавляет новое выражение в предикат. Может ли кто-нибудь объяснить это? (Я видел этот вопрос, я не хочу, чтобы там был общий ответ. Я ищу конкретное объяснение того, как Expression.Invoke и Expression.Lambda создают новое выражение).

public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                     Expression<Func<T, bool>> expr2)
{
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
        (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
4b9b3361

Ответ 1

Скажем, у вас есть:

Expression<Func<Person, bool>> isAdult = p1 => p1.Age >= 18;

// I've given the parameter a different name to allow you to differentiate.
Expression<Func<Person, bool>> isMale = p2 => p2.Gender == "Male";

И затем объедините их с PredicateBuilder

var isAdultMale = isAdult.And(isMale);

Что выражает PredicateBuilder - это выражение, которое выглядит так:

// Invoke has no direct equivalent in C# lambda expressions.
p1 => p1.Age >= 18 && Invoke(p2 => p2.Gender == "Male", p1)

Как вы можете видеть:

  • В результате лямбда повторно использует параметры первого выражения.
  • Имеет тело, которое вызывает второе выражение, передавая параметры первого выражения в качестве замены для вторых параметров выражения. Результат InvocationExpression похож на эквивалент выражения вызова метода (вызов процедуры путем передачи аргументов параметров).
  • And первое тело выражения и это InvocationExpression вместе, чтобы создать тело полученной лямбда.

Идея заключается в том, что поставщик LINQ должен уметь понимать семантику этой операции и принимать разумный ход действий (например, генерировать SQL, например WHERE age >= 18 AND gender = 'Male').

Часто провайдеры имеют проблемы с InvocationExpression s из-за очевидных осложнений обработки "вложенного выражения-вызова внутри выражения".

Чтобы обойти это, LINQKit также предоставляет помощник Expand. Это, по сути, "inlines" вызов вызова умно, заменив вызов телом вложенного выражения, соответствующим образом заменив использование вложенных параметров выражения (в этом случае заменив p2 на p1). Это должно вызвать что-то вроде:

p1 => p1.Age >= 18 && p1.Gender == "Male"

Обратите внимание, что это, как вы бы вручную объединили эти предикаты, если бы вы сделали это самостоятельно в лямбда. Но с LINQKit, вы можете получить эти предикаты из независимых источников и легко объединить их:

  • Без написания кода выражения "вручную".
  • Необязательно, таким образом, чтобы это было прозрачно для потребителей полученной лямбда.