Search code examples
c#expression-trees

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


Solution

  • 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.