2012-05-16 9 views
13

que tienen dos expresiones lambda:¿Cómo puedo combinar dos expresiones lambda sin usar el método Invoke?

Expression<Func<MyEntity, bool>> e1 = i=>i.FName.Contain("john");

y

Expression<Func<MyEntity, bool>> e2=i=>i.LName.Contain("smith"); 

del tipo I, viene de mis entidades POCO, que no se utilizan con invocación. Quiero combinar estos en tiempo de ejecución.

quiero combinar estas expresiones en tiempo de ejecución en forma similar a como:

Expression<Func<MyEntity, bool>> e3 = Combine(e1,e2); 
+0

Combinar * cómo * - "y también"? –

+0

sí, y también. Quiero combinar estos en tiempo de ejecución. – PickleRick

Respuesta

35

El problema es que se puede no sólo "y"/"o" ​​ellos, porque es necesario volver a escribir las partes internas para cambiar los parámetros; si usa .Body desde e1, pero el parámetro desde e2, no funcionará, porque el .Body de e1 hace referencia a una instancia de parámetro completamente no relacionada que no está definida. Esto es más obvia si utiliza:

Expression<Func<MyEntity, bool>> e1 = i => i.FName.Contains("john"); 
Expression<Func<MyEntity, bool>> e2 = j => j.LName.Contains("smith"); 

(tenga en cuenta la diferencia entre e1 usando i y e2 usando j)

Si los combinamos sin reescribir el parámetro, que se pueden conseguir los esperpentos:

Expression<Func<MyEntity, bool>> combined = 
     i => i.FName.Contains("john") && j.LName.Contains("smith"); 

(woah .... ¿de dónde vienen de j?)

SIN EMBARGO; el problema es idéntico independientemente del nombre del parámetro: sigue siendo un parámetro diferente.

Y como la expresión es inmutable, no puede simplemente cambiarla "en su lugar".

El truco es usar un "visitante" para reescribir los nodos, así:

using System; 
using System.Linq.Expressions; 

class SwapVisitor : ExpressionVisitor 
{ 
    private readonly Expression from, to; 
    public SwapVisitor(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     return node == from ? to : base.Visit(node); 
    } 
} 

static class Program 
{ 
    static void Main() 
    { 
     Expression<Func<MyEntity, bool>> e1 = i => i.FName.Contains("john"); 
     Expression<Func<MyEntity, bool>> e2 = i => i.LName.Contains("smith"); 

     // rewrite e1, using the parameter from e2; "&&" 
     var lambda1 = Expression.Lambda<Func<MyEntity, bool>>(Expression.AndAlso(
      new SwapVisitor(e1.Parameters[0], e2.Parameters[0]).Visit(e1.Body), 
      e2.Body), e2.Parameters); 

     // rewrite e1, using the parameter from e2; "||" 
     var lambda2 = Expression.Lambda<Func<MyEntity, bool>>(Expression.OrElse(
      new SwapVisitor(e1.Parameters[0], e2.Parameters[0]).Visit(e1.Body), 
      e2.Body), e2.Parameters); 
    } 
} 
+0

+1 Guau, qué respuesta tan perspicaz. –

+0

¡Gracias, marca! lo hiciste. funciona muy bien – PickleRick