23

I have two lambda expressions:

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

and

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

the i type, comes from my poco entities, that can't used with invoke. I want to combine these in runtime.

I want to combine these expressions in runtime in a similar way as:

Expression<Func<MyEntity, bool>> e3 = Combine(e1,e2);
1
  • yes, andAlso. I want to combine these in runtime. Commented May 16, 2012 at 7:41

2 Answers 2

64

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);
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Brilliant answer! Spent two days trying to implement this, and other answers contained loads of code. Thanks a lot, Mark!
Wow! Amazing stuff
-3

Maybe with a simple if statement?

using (MyDataBaseEntities db = new MyDataBaseEntities())
{
if (db.People.Any(p => p.FirstName == FirstNameText.Text && p.LastName == LastNameText.Text))
    {
        //Do something
    }
}    

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.