Jekas
Jekas

Reputation: 578

c# expressions combine property expression with lambda expression

Please help to solve issue below:

public class TestParent
{
    public int Number { get; set; }
}

public class Test
{
    public TestParent Parent { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Expression<Func<TestParent, bool>> parentExpression = x => x.Number == 10;
        Expression<Func<Test, TestParent>> testExpression = x => x.Parent;

        var test = new Test {Parent = new TestParent {Number = 10}};

        Expression<Func<Test, bool>> composedExpression = ?; // x => x.Parent.Number == 10

        bool result = composedExpression.Compile()(test);

        if (result)
        {
            Console.WriteLine("Test passed!");
        }
    }
}

Upvotes: 1

Views: 1654

Answers (3)

Servy
Servy

Reputation: 203812

We can create a Compose method for expressions like so:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

This is using the following helper method to replace all instance of one expression with another:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

This allows you to write:

Expression<Func<Test, bool>> composedExpression = 
    testExpression.Compose(parentExpression);

Upvotes: 3

Jekas
Jekas

Reputation: 578

Get this done:

var composedExpression = testExpression.Combine(parentExpression, true);

where Combine implementation is from: Combining two lambda expressions in c#

Upvotes: 0

Maarten
Maarten

Reputation: 22945

I think you are trying to create a new expression which consist of the logic which is now defined in the two separate expressions parentExpression and testExpression.

Unfortunately you cannot combine expressions like that easily (without breaking down the expressions and using the internal expression-bodies) because the parameters of the expressions are different, and you have to manually create the expression from the contents of the two expressions. You cannot use the two expressions as they are, and combine them.

You can compile the two expressions, and use them in a new expression. It would be something like this. But be warned, the composedExpression will be nothing more than an invoke of the compiled expressions. It will not contain the logic which is now defined in the other two expressions.

Expression<Func<TestParent, bool>> parentExpression = x => x.Number == 10;
Expression<Func<Test, TestParent>> testExpression = x => x.Parent;

var parentExpressionCompiled = parentExpression.Compile();
var testExpressionCompiled = testExpression.Compile();

var test = new Test {Parent = new TestParent {Number = 10}};

Expression<Func<Test, bool>> composedExpression = x => parentExpressionCompiled(testExpressionCompiled(x));

bool result = composedExpression.Compile()(test);

if (result) {
    Console.WriteLine("Test passed!");
}

Upvotes: 0

Related Questions