Expression Tree Copy or Convert
Asked Answered
B

3

16

How to convert a ExpressionTree of form

Expression<Func<POCO1, bool>> exp = p => p.Age > 50;

to

Expression<Func<POCO2, bool>> exp2 = p => p.Age > 50;

where POCO1 and POCO2 are C# objects and both have Int32 Age property

Breazeale answered 5/1, 2011 at 7:49 Comment(1)
What are you trying to do? I mean, why do you want to copy it like that?Haerr
S
26

well, you can make custom expression visitor that will replace parameter references and patch member access expressions

class Converter<TTo>
{
    class ConversionVisitor : ExpressionVisitor
    {
        private readonly ParameterExpression newParameter;
        private readonly ParameterExpression oldParameter;

        public ConversionVisitor(ParameterExpression newParameter, ParameterExpression oldParameter)
        {
            this.newParameter = newParameter;
            this.oldParameter = oldParameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return newParameter; // replace all old param references with new ones
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression != oldParameter) // if instance is not old parameter - do nothing
                return base.VisitMember(node);

            var newObj = Visit(node.Expression);
            var newMember = newParameter.Type.GetMember(node.Member.Name).First();
            return Expression.MakeMemberAccess(newObj, newMember);
        }
    }

    public static Expression<Func<TTo, TR>> Convert<TFrom, TR>(
        Expression<Func<TFrom, TR>> e
        )
    {
        var oldParameter = e.Parameters[0];
        var newParameter = Expression.Parameter(typeof(TTo), oldParameter.Name);
        var converter = new ConversionVisitor(newParameter, oldParameter);
        var newBody = converter.Visit(e.Body);
        return Expression.Lambda<Func<TTo, TR>>(newBody, newParameter);
    }
}

class A
{
    public int Value { get; set; }
}

class B
{
    public int Value { get; set; }
}

Expression<Func<A, int>> f = x => x.Value;
var f2 = Converter<B>.Convert(f);
Stridulate answered 5/1, 2011 at 9:32 Comment(0)
J
1

Rough Steps:


 Get the expression Cast it to BinaryExpression
 Get the left operand Cast it to MemberExpression
 Get the Underlying Type that the property belong to 
 Change it to your new type if you can.

The type you get here is a property without setter as I guessed.

Expression<Func<MainWindow, bool >> exp1 = o => this.ActualHeight>50;
var type = ((MemberExpression)((BinaryExpression)exp1.Body).Left).Expression.Type;

So you must build a new expression

Here is the way

manually build linq expression for x => x.Child == itemToCompare.Child

Jacquenette answered 5/1, 2011 at 7:53 Comment(0)
U
-1

Ideally - you don't. Make an interface that describes the Age property, and build the expression to refer to that. If you can't modify the POCO types, use a language like Go, where interfaces are implicit :-).

Ultimogeniture answered 6/1, 2011 at 8:52 Comment(2)
There are way more constraints, than having full control over design of POCO1 and POCO2. In a practical example the Expression would go into an API like Entity Framework, (which we also do not control over) and Expression built on the interface will not be type compatible with the expected, even POCO1 is a IPOCO and POCO2 is a IPOCO. (Expression<Func<POCO1, bool>> is NOT Expression<Func<IPOCO, bool>> even POCO1 is a IPOCO. Regarding the other part of the answer, recommending using an other programming language also does not help.Keratitis
Yes, that was tongue-in-cheek. However, often enough you can use an Expression<Func<IPOCO, bool>> instead of either a POCO1 or POCO2 consuming lambda equivalently - and if you can, you can sidestep this complexity entirely.(Unfortunately, the question does not explain the motivation here).Ultimogeniture

© 2022 - 2024 — McMap. All rights reserved.