¿Cómo funciona PredicateBuilder?

.net c# expression-trees linq predicatebuilder

Pregunta

C # en pocas palabras tiene una clase gratuita llamada PredicateBuilder que construye los predicados LINQ pieza por pieza disponible aquí . Aquí hay un extracto del método que agrega una nueva expresión al predicado. ¿Podría alguien explicarlo? (He visto esta pregunta , no quiero una respuesta general como allí. Estoy buscando una explicación específica de cómo Expression.Invoke y Expression.Lambda construyen la nueva expresión).

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

Respuesta aceptada

Digamos que tienes:

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";

Y luego combínalos con PredicateBuilder

var isAdultMale = isAdult.And(isMale);

Lo que PredicateBuilder produce es una expresión que se ve así:

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

Como puedes ver:

  1. La lambda resultante reutiliza los parámetros de la primera expresión.
  2. Tiene un cuerpo que invoca la segunda expresión al pasar los parámetros de la primera expresión como reemplazo de los parámetros de la segunda expresión. La InvocationExpression resultante es algo así como la expresión equivalente de una llamada al método (llamar a una rutina pasando argumentos para los parámetros).
  3. And es el cuerpo de la primera expresión y esta expresión de InvocationExpression juntas para producir el cuerpo de la lambda resultante.

La idea es que el proveedor de LINQ pueda entender la semántica de esta operación y tomar un curso de acción razonable (p. Ej., Generar SQL como WHERE age >= 18 AND gender = 'Male' ).

Sin embargo, a menudo, los proveedores tienen problemas con InvocationExpression debido a las complicaciones obvias de procesar una 'llamada de expresión anidada dentro de una expresión'.

Para solucionar esto, LINQKit también proporciona el ayudante Expand . Esto esencialmente "inline" la llamada de invocación de manera inteligente al reemplazar la llamada con el cuerpo de la expresión anidada, sustituyendo los usos de los parámetros de la expresión anidada de manera apropiada (en este caso, reemplazando p2 con p1 ). Esto debería producir algo como:

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

Tenga en cuenta que, de esta forma, habría combinado manualmente esos predicados si lo hubiera hecho usted mismo en un lambda. Pero con LINQKit, puedes obtener estos predicados de fuentes independientes y combinarlos fácilmente:

  1. Sin escribir el código de expresión "a mano".
  2. Opcionalmente, de una manera que sea transparente para los consumidores de la lambda resultante.


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow