2012-05-16 8 views
13

ho due espressioni lambda:Come posso combinare due espressioni lambda senza utilizzare il metodo Invoke?

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

e

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

di tipo I, viene dal mio enti Poco, che non possono usati con invoke. Voglio combinarli in runtime.

voglio combinare queste espressioni in fase di esecuzione in maniera simile a come:

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

Combine * come * - "e anche"? –

+0

sì, e anche. Voglio combinarli in runtime. – PickleRick

risposta

35

Il problema è che è possibile non solo "e"/"o" ​​loro, perché è necessario riscrivere gli interni per cambiare i parametri; se si utilizzada e1, ma il parametro da e2, non funzionerà, poiché lo .Body di e1 fa riferimento a un'istanza di parametro completamente indipendente che non è definita. Questo è più evidente se si utilizza:

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

(notare la differenza tra e1 utilizzando i e e2 utilizzando j)

Se li combiniamo senza dover riscrivere il parametro, si otterrebbe l'assurdo:

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

(woah .... dove ha j provengono da?)

TUTTAVIA; il problema è identico indipendentemente dal nome del parametro : è ancora un parametro diverso.

E poiché l'espressione è immutabile, non è possibile scambiarla "sul posto".

Il trucco è quello di utilizzare un "visitatore" per riscrivere i nodi, in questo modo:

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 Wow - che risposta perspicace. –

+0

Grazie! l'hai fatta. funziona molto bene – PickleRick

Problemi correlati