Convert Expression trees
Asked Answered
G

1

12

let there be :

Expression<Func<Message, bool>> exp1 = x => x.mesID == 1;
Expression<Func<MessageDTO, bool>> exp2 = x => x.mesID == 1;

now i need to pass exp1 to _db.Messages.where(exp1); problem is i only have exp2, i need to convert the type to Message , All properties are the same !

now i do this :

  var par = Expression.Parameter(typeof(Message));
  var ex = (Expression<Func<Message, bool>>)Expression.Lambda(exp2.Body, par);

problem with this is the input paramter gets changed yes ! but the x inside the body of the lambda "x.mesID" is of the old type.

any way to change all the parameters type in the body too or change the input parameter in away it reflect the body too ?

i guess this is a big problem i always have with LINQ , since between layers i cant pass the generated classes , as this will make layers coupled , so i have to make light weight classes , now how do i use a method like _db.Messages.where(); from busiess layer ?!! while busniess layer doesnt know anything about Message type it only know MessageDTO.

Grievance answered 8/9, 2010 at 12:11 Comment(0)
F
14

No, basically. Expression trees are immutable, and contain full member meta-data (i.e. that mesID is messageDTO.mesID). To do this, you would have to rebuild the expression tree from scratch (via a visitor), handling every node type you need to support.

If the expression tree is basic this should be OK, but if you need to support the entire gamut? a huge PITA (especially in .NET 4, which adds a lot more node-types).


A basic example that does just what is required for the example; you would need to add more node-types for more complex expressions:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
static class Program
{
    static void Main()
    {
        Expression<Func<Message, bool>> exp1 = x => x.mesID == 1;
        var exp2 = Convert<Message, MessageDTO>(exp1);
    }
    static Expression<Func<TTo, bool>> Convert<TFrom, TTo>(Expression<Func<TFrom, bool>> expr)
    {
        Dictionary<Expression,Expression> substitutues = new Dictionary<Expression,Expression>();
        var oldParam = expr.Parameters[0];
        var newParam = Expression.Parameter(typeof(TTo), oldParam.Name);
        substitutues.Add(oldParam, newParam);
        Expression body = ConvertNode(expr.Body, substitutues);
        return Expression.Lambda<Func<TTo,bool>>(body, newParam);
    }
    static Expression ConvertNode(Expression node, IDictionary<Expression, Expression> subst)
    {
        if (node == null) return null;
        if (subst.ContainsKey(node)) return subst[node];

        switch (node.NodeType)
        {
            case ExpressionType.Constant:
                return node;
            case ExpressionType.MemberAccess:
                {
                    var me = (MemberExpression)node;
                    var newNode = ConvertNode(me.Expression, subst);
                    return Expression.MakeMemberAccess(newNode, newNode.Type.GetMember(me.Member.Name).Single());
                }
            case ExpressionType.Equal: /* will probably work for a range of common binary-expressions */
                {
                    var be = (BinaryExpression)node;
                    return Expression.MakeBinary(be.NodeType, ConvertNode(be.Left, subst), ConvertNode(be.Right, subst), be.IsLiftedToNull, be.Method);
                }
            default:
                throw new NotSupportedException(node.NodeType.ToString());
        }
    }
}
class Message { public int mesID { get; set; } }
class MessageDTO { public int mesID { get; set; } }
Franko answered 8/9, 2010 at 12:17 Comment(7)
what do you mean by via vistor ? can you give me an example plz ?Grievance
A visitor implementation; i.e. some code construct that you use to traverse the entire tree structure, typically constructing an alternative tree (from the leaf nodes back to the root, since each branch is immutable). This might boil down to a huge switch (on node-type), with recursive handling for each node type. I'll try to whip up an example...Franko
thanks Marc it worked i just have to make it support more ExpressionTypeGrievance
@Grievance - that is the fun bit ;p Note that you should be able to re-use large chunks; for example most of the binary expressions should be able to use the existing example.Franko
just one question i have for you. how do you handle case ExpressionType.Convert:Grievance
@MarcGravell do you have any idea about ExpressionType.New too?Oriole
What @Grievance asked " how do you handle case ExpressionType.Convert" This throws an exception as written.Stanwin

© 2022 - 2024 — McMap. All rights reserved.