Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change a type in an expression tree?

I have a method like this:

private bool Method_1(Expression<Func<IPerson, bool>> expression)
{
    /* Some code that will call Method_2 */
}

In this method I want to change the IPerson type to another type. I want to call another method that looks like this:

private bool Method_2(Expression<Func<PersonData, bool>> expression)
{
    /* Some code */
}

So, in method_1 I need to change IPerson to PersonData. How can I do this?

Edit:

When I call: Method_1(p => p.Id == 1) I want to 'save' the condition (p.Id == 1) but I want to execute this condition on another type, namely IPerson. So, I need to alter the expression or create a new expression with IPerson

like image 739
Martijn Avatar asked Feb 18 '13 09:02

Martijn


People also ask

How does an expression tree work?

Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y . You can compile and run code represented by expression trees.

What is more complex expression tree?

That's the basics of building an expression tree in memory. More complex trees generally mean more node types, and more nodes in the tree. Let's run through one more example and show two more node types that you will typically build when you create expression trees: the argument nodes, and method call nodes.

How do you run expression tree?

Expression trees that represent lambda expressions are of type LambdaExpression or Expression<TDelegate>. To execute these expression trees, call the Compile method to create an executable delegate, and then invoke the delegate.


1 Answers

It is easy if you use .net 4 (update: as noted in comment ExpressionVisitor was added in version 4 not 4.5) it would require some googling for older frameworks:

There are some assumptions but I think they are valid for your DTO and Entity scenario - properties accessed must match.

class PersonData
{
    public bool Prop { get; set; }
}

interface IPerson 
{
    bool Prop { get; set; }
}

In .net 4 there is ExpressionVisitor class defined that makes this a lot easier if you use older one then you need to write or find implementation of it:

class Visitor<T> : ExpressionVisitor
{
    ParameterExpression _parameter;

    //there must be only one instance of parameter expression for each parameter 
    //there is one so one passed here
    public Visitor(ParameterExpression parameter)
    {
        _parameter = parameter;
    }

    //this method replaces original parameter with given in constructor
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return _parameter;
    }

    //this one is required because PersonData does not implement IPerson and it finds
    //property in PersonData with the same name as the one referenced in expression 
    //and declared on IPerson
    protected override Expression VisitMember(MemberExpression node)
    {
        //only properties are allowed if you use fields then you need to extend
        // this method to handle them
        if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
            throw new NotImplementedException();

        //name of a member referenced in original expression in your 
        //sample Id in mine Prop
        var memberName = node.Member.Name;
        //find property on type T (=PersonData) by name
        var otherMember = typeof(T).GetProperty(memberName);
        //visit left side of this expression p.Id this would be p
        var inner = Visit(node.Expression);
        return Expression.Property(inner, otherMember);
    }
}

Proof of concept:

class Program
{
   static void Main()
    {
        //sample expression
        Expression<Func<IPerson, bool>> expression = x => x.Prop;

        //parameter that will be used in generated expression
        var param = Expression.Parameter(typeof(PersonData));
        //visiting body of original expression that gives us body of the new expression
        var body = new Visitor<PersonData>(param).Visit(expression.Body);
        //generating lambda expression form body and parameter 
        //notice that this is what you need to invoke the Method_2
        Expression<Func<PersonData, bool>> lambda = Expression.Lambda<Func<PersonData, bool>>(body, param);
        //compilation and execution of generated method just to prove that it works
        var boolValue = lambda.Compile()(new PersonData());
    }
}

Note that this will work for simple expressions. If you need to handle x.Prop.Prop1 < 3 then you need to extend this further.

like image 115
Rafal Avatar answered Nov 07 '22 01:11

Rafal