2012-05-16 29 views
13

Mam dwóch wyrażeń lambda:Jak mogę połączyć dwa wyrażenia lambda bez użycia metody Invoke?

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

i

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

typu I, pochodzi z moich poco podmiotów, które nie może być używany z powołaniem. Chcę je połączyć w czasie wykonywania.

chcę połączyć te wyrażenia w czasie wykonywania w podobny sposób, jak:

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

Kombajny * jak * - "a także"? –

+0

tak, andAlso. Chcę je połączyć w czasie wykonywania. – PickleRick

Odpowiedz

35

Problem polega na tym, że nie można po prostu „i”/„lub” je, bo trzeba re-write elementy wewnętrzne do zmiany parametrów; jeśli użyjesz .Body z e1, ale parametr z e2, to nie zadziała - ponieważ .Body z e1 odnosi się do zupełnie niepowiązanej instancji parametru, która nie jest zdefiniowana. Jest to bardziej oczywiste jeśli używasz:

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

(zwrócić uwagę na różnicę pomiędzy e1 użyciu i i e2 użyciu j)

Jeśli połączymy je bez przepisywania parametru, chcielibyśmy uzyskać bezsensowne:

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

(woah .... skąd j pochodzi?)

JEDNA; problem jest identyczny, niezależnie od wartości parametru : nadal jest to inny parametr.

A ponieważ wyrażenie jest niezmienne, nie można po prostu zamienić go "na miejscu".

Sztuką jest wykorzystanie "gościem" przepisać węzły, tak:

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 - co za wnikliwa odpowiedź. –

+0

Dziękuję znak! zrobiłeś to. działa bardzo dobrze. – PickleRick