How can I combine two lambda expressions without using Invoke method?
The problem is that you can't just "and"/"or" them, because you need to re-write the internals to change the parameters; if you use the .Body
from e1
, but the parameter from e2
, it won't work - because the .Body
of e1
references a completely unrelated parameter instance that isn't defined. This is more obvious if you use:
Expression<Func<MyEntity, bool>> e1 = i => i.FName.Contains("john");
Expression<Func<MyEntity, bool>> e2 = j => j.LName.Contains("smith");
(note the difference between e1
using i
and e2
using j
)
If we combine them without rewriting the parameter, we would get the nonsensical:
Expression<Func<MyEntity, bool>> combined =
i => i.FName.Contains("john") && j.LName.Contains("smith");
(woah.... where did j
come from?)
HOWEVER; the problem is identical regardless of the name of the parameter: it is still a different parameter.
And since the expression is immutable you can't just swap it "in place".
The trick is to use a "visitor" to rewrite the nodes, like so:
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);
}
}