Как работает PredicateBuilder
C# в 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);
}
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
помощник. Это, по сути, "встраивает" вызов вызова в умное состояние, заменяя вызов телом вложенного выражения, соответственно заменяя использование параметров вложенного выражения (в данном случае заменяя p2
с p1
). Это должно произвести что-то вроде:
p1 => p1.Age >= 18 && p1.Gender == "Male"
Обратите внимание, что таким образом вы бы вручную объединили эти предикаты, если бы сделали это сами в лямбда-выражении. Но с помощью LINQKit вы можете получить эти предикаты из независимых источников и легко объединить их:
- Без написания "вручную" кода выражения.
- Необязательно, таким образом, чтобы это было прозрачно для потребителей получаемой лямбды.