2010-03-01 18 views
8

¿Cómo hago para unir dos expresiones lambda como theese:lambda Expresiones

Expression<Func<string, bool>> expr1 = a => a.Length > 100; 
Expression<Func<string, bool>> expr2 = b => b.Length < 200; 

... en una expresión como esta:

Expression<Func<string, bool>> expr3 = s => s.Length < 100 && s.Length < 200; 

Es decir, unirse a ellos con un operador AndAlso . (O cualquier otro operador para el caso ...)

En realidad tuve éxito con algunos desagradables recursivos reemplazo de parámetros lambda y luego unirse con el método Expression.AndAlso. Pero estoy buscando algo más simple.

Por ejemplo algo como: (. Lo que obviamente no funciona)

Expression<Func<string, bool>> expr3 = c => expr1(a) && expr2(b); 
+0

Sólo me preguntaba, ¿por qué no se utiliza puede Expresión > expr3 = s => s.Length <100 && s.Length <200 ;? –

+0

Se debe a que las expresiones se generan en función de la configuración del sistema. De hecho, necesito unirme a una lista de expresiones para una expresión final. – LaZe

Respuesta

2

Su "algo así como" funcionaría si se trata de delegados normales. Pero si tiene que usar árboles de expresión, no veo ninguna otra solución que el reemplazo recursivo.

En .NET 4, puede usar System.Linq.Expressions.ExpressionVisitor para hacer este tipo de reemplazo recursivo mucho más fácil. Para .NET 3.5, eche un vistazo a esta muestra: http://msdn.microsoft.com/en-us/library/bb882521.aspx

Al usar ExpressionVisitor, solo tiene que anular los métodos para los tipos de nodo que desea reemplazar y el árbol circundante se reconstruirá automáticamente.

Si se trata de condiciones para usar con LINQ, una solución mucho más fácil para combinar dinámicamente las condiciones es simplemente llamar a Where() varias veces.

+0

Acabo de comprobar y ExpressionVisitor funciona muy bien para esto. No puedo esperar a que se publique 4.0. – LaZe

1

Acabo de descubrir cómo hacer esto con .NET 4 usando un nuevo método de actualización. Dado que es un método nuevo, supongo que también deben haberlo necesitado. Estoy muy contento con eso, porque la solución de .NET 3.5 es realmente fea. (Nota: esta solución no funciona de todos modos Compruebe comentarios..)

Expression<Func<string, bool>> expr1 = a => a.Length > 100; 
Expression<Func<string, bool>> expr2 = b => b.Length < 200; 

// This produces a new expression where the parameter b is replaced with a 
expr2 = expr2.Update(expr1.Body, expr1.Parameters); 

// So now we can join the bodies and produce a new lambda expression. 
Expression<Func<string, bool>> expr3 = Expression.Lambda<Func<string, bool>>(Expression.AndAlso(expr1.Body, expr2.Body), expr1.Parameters); 
+0

Creo que malinterpretas lo que hace Update. Simplemente crea una nueva LambdaExpression con el mismo tipo, nombre y opciones de tailcall, pero con un cuerpo y conjunto de parámetros diferentes. No hace ningún reemplazo dentro de los cuerpos lamdba. Ejecutando las salidas de código de muestra "a => ((a.Length> 100) AndAlso (a.Length> 100))" para expr3, que no es lo que desea. – Daniel

+0

Tristemente, usted está en lo correcto Daniel ... así que volví a mi desagradable solución :-( – LaZe

2

No es tan malo con Expression.Invoke ...:

var strings = (new [] { "a", "bb", "ccc", "dddd", "eeeee", "fffff" }); 
Expression<Func<string, bool>> expr1 = a => a.Length > 1; 
Expression<Func<string, bool>> expr2 = b => b.Length < 4; 

ParameterExpression p = expr1.Parameters[0]; 

var andAlso = System.Linq.Expressions.Expression.AndAlso(Expression.Invoke(expr1, p), Expression.Invoke(expr2, p)); 
var lambda = LambdaExpression.Lambda<Func<string, bool>>(andAlso, p); 
var filteredStrings = strings.AsQueryable().Where(lambda); 
+0

Gracias por la sugerencia de Richard. Voy a estudiar esa idea. Desde que convierto las Expresiones a SQL (entre otras cosas) me ' Necesitaré apoyar InvokeExpression en varios lugares de mi sistema. Sin embargo, es mejor que mi solución actual ... – LaZe

+0

Me di cuenta de que esto podría ayudarme a resolver un problema que tenía hace un rato y que estaba totalmente atascado ... ¡Salud! –