Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I combine two Expressions?

I am trying to build up an expression that will be applied to an IQueryable collection.

I can build an expression like this:

[TestClass]
public class ExpressionTests
{
    private IQueryable<MyEntity> entities;

    private class MyEntity
    {
        public string MyProperty { get; set; }
    }

    [TestInitialize]
    public void Setup()
    {
        entities = new[]
                    {
                        new MyEntity {MyProperty = "first"}, 
                        new MyEntity {MyProperty = "second"}
                    }.AsQueryable();
    }

    [TestMethod]
    public void TestQueryingUsingSingleExpression()
    {
        Expression<Func<MyEntity, bool>> expression = e => e.MyProperty.Contains("irs");
        Assert.AreEqual(1, entities.Where(expression).Count());
    }
}

Now I want to separate the two parts of the expression:

[TestMethod]
public void TestQueryingByCombiningTwoExpressions()
{
    Expression<Func<MyEntity, string>> fieldExpression = e => e.MyProperty;
    Expression<Func<string, bool>> operatorExpression = e => e.Contains("irs");
    // combine the two expressions somehow...
    Expression<Func<MyEntity, bool>> combinedExpression = ???;

    Assert.AreEqual(1, entities.Where(combinedExpression).Count());
}

Any suggestions as to how I might do this?

Btw the provider that will be resolving the expression is Linq for NHibernate.

like image 746
Jonathan Moffatt Avatar asked Jul 28 '11 06:07

Jonathan Moffatt


2 Answers

Take a look at your two expression trees:

                 |                                      |
               Lambda                                Lambda
              /      \                              /      \
             /        \                            /        \
     Property          Parameter x               Call        Parameter y
    /        \                                  /  |  \
   /          \                                /   |   \
  x           MyProperty              EndsWidth    y    Constant
                                                        |
                                                       "5"

You need to create a new tree that looks like this:

                                 |
                               Lambda
                              /      \
                             /        \
                           Call        Parameter z
                          /  |  \
                         /   |   \
                   EndsWith  |   Constant
                             |         \
                          Property     "5"
                         /        \
                        /          \
                       z          MyProperty

You can easily see what parts of the new tree come from which original tree.

To create the tree, you take the body of the second lambda expression (Call) and replace all occurrences of y with the body of the first lambda expression (Property) and all occurrences of x with z. Then you wrap the result in a new lambda expression with parameter z.

You can use the ExpressionVisitor Class to rewrite the tree, and the Expression.Lambda Method to create the new lambda expression.

like image 190
dtb Avatar answered Sep 28 '22 03:09

dtb


It depends on what the provider supports; if it supports sub-expressions (LINQ-to-SQL does, EF doesn't; I don't know about NH), then:

var combinedExpression = Expression.Lambda<Func<MyEntity, bool>>(
       Expression.Invoke(operatorExpression, fieldExpression.Body),
       fieldExpression.Parameters);

however, if it doesn't you'll need to use ExpressionVisitor to merge them.

like image 39
Marc Gravell Avatar answered Sep 28 '22 01:09

Marc Gravell