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

C# в двух словах имеет свободный класс PredicateBuilder, который создает предикаты LINQ по частямздесь. Вот выдержка метода, который добавляет новое выражение к предикату. Кто-нибудь может это объяснить? (Я видел этот вопрос, мне не нужен общий ответ, как там. Я ищу конкретное объяснение того, как выражение.Вызов и выражение.Лямбда построить новое выражение).

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);
}

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)

Как видите:

  1. в результате лямбда использует параметры первого выражения.
  2. тело вызывает второе выражение путем передачи параметров первого выражения в качестве замены для параметров второго выражения. В результате InvocationExpression похоже на выражение-эквивалент вызова метода (вызов подпрограммы путем передачи аргументов для параметров).
  3. Ands тело первого выражения и это InvocationExpression вместе, чтобы произвести тело результирующей лямбды.

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

часто, однако, у поставщиков есть проблемы с InvocationExpressions, из-за очевидных осложнений обработки " вложенного выражения-вызова внутри выражения.'

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

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

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

  1. без написания кода выражения "от руки".
  2. выборочно, в путе который прозрачен к потребителям в результате лямда.