tags:

views:

150

answers:

3

Hello,

How would I go about joining two lambda expressions like theese:

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

... into an expression like this:

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

That is, joining them with an AndAlso operator. (Or any other operator for that matter...)

I actually succeeded with some nasty recursive replacement of lambda parameters and then joining with the Expression.AndAlso method. But I'm looking for something more simple.

For example something like: (Which obviously doesn't work.)

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

Your "something like" would work if you were dealing with normal delegates. But if you have to use expression trees, I don't see any other solution than recursive replacement.

In .NET 4, you can use the System.Linq.Expressions.ExpressionVisitor to make this kind of recursive replacement much easier. For .NET 3.5, take a look at this sample: http://msdn.microsoft.com/en-us/library/bb882521.aspx

Using the ExpressionVisitor, you only have to override methods for the node types you want to replace and the surrounding tree will be automatically reconstructed.

If you are dealing with conditions for use with LINQ, a much easier solution to dynamically combine conditions is to just call Where() multiple times.

Daniel
I just checked and ExpressionVisitor works great for this. Can hardly wait for 4.0 to be released.
LaZe
+1  A: 

I just discovered how to do this with .NET 4 using a new Update method. Since it's a new method I suppose they must have needed it also. I'm really happy with that, because med .NET 3.5 solution is really ugly. (NOTE: This solution doesn't work anyway. Check comments.)

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);
LaZe
I think you misunderstand what Update does. It merely creates a new LambdaExpression with the same type, name and tailcall options but with a different body and parameter set. It does not do any replacement within the lamdba bodies.Running your sample code outputs "a => ((a.Length > 100) AndAlso (a.Length > 100))" for expr3, which is not what you want.
Daniel
Sadly you are correct Daniel... so I'm back to my nasty solution :-(
LaZe
+1  A: 

It's not too bad with 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);
Richard Hein
Thanks for the suggestion Richard. I'll look into that idea. Since I convert the Expressions to SQL (among other things) I'll need to support InvokeExpression several places in my system. It's nicer than my current solution though...
LaZe
I just realized this could help me solve a problem I had a little while ago and was totally stuck on.... Cheers!
Richard Hein