Search code examples
c#reflectionexpression-trees

Complex Expression Tree with no changing context of param


i need to dinamically generate expression like this:

Expression<Func<MyClass, bool>> expr = x => (x.SomeField.CompareTo(someValue) <= 0);

trying to do it like this:

var paramExpr = Expression.Parameter(typeof(MyClass), "x");
Expression<Func<MyClass, FieldType>> pathToField = x => x.SomeField;
Expression path = pathToField;
if (!(path is LambdaExpression lambdaMember))
    throw ...;
Expression valueExpr = Expression.Constant(someValue);
var bodyExpr = Expression.LessThanOrEqual(Expression.Call(lambdaMember.Body, "CompareTo", null, valueExpr ), Expression.Constant(0));
return Expression.Lambda<Func<MyClass, FieldType>>(bodyExpr, paramExpr);

but always getting error when trying to compile this:

variable 'x' of type 'MyClass' referenced from scope '', but it is not defined

how i could do this correctly?


Solution

  • The problem here is that you're using lambdaMember.Body, which references the x from x => x.SomeField - but because you only used the .Body, that is undefined - and is unrelated to the x from Expression.Parameter(typeof(MyClass), "x");

    In the general case, there are 2 options here:

    • invoke the entire lambda (i.e. lambdaMember, not lambdaMember.Body) - passing in the arguments to use for the parameters
    • rewrite the inner lambda at runtime using ExpressionVisitor - swapping out instances of the x from the inner expression with whatever you wanted to use as the argument - presumably paramExpr

    The first option is easier, and is just Expression.Invoke:

    var bodyExpr = Expression.LessThanOrEqual(
        Expression.Call(Expression.Invoke(lambdaMember, paramExpr),
        "CompareTo", null, valueExpr), Expression.Constant(0));
    

    Note: there is a third option in this case, since it is a relatively simple example - you can just hijack the parameter from the inner expression and use it instead of declaring paramExpr as a new parameter expression:

    var paramExpr = lambdaMember.Parameters.Single();
    Expression valueExpr = Expression.Constant(someValue);
    var bodyExpr = Expression.LessThanOrEqual(
        Expression.Call(lambdaMember.Body,
        "CompareTo", null, valueExpr), Expression.Constant(0));
    return Expression.Lambda<Func<MyClass, FieldType>>(bodyExpr, lambdaMember.Parameters);