【问题标题】:Convert Expression trees转换表达式树
【发布时间】:2011-04-09 17:09:29
【问题描述】:

要有:

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

现在我需要将 exp1 传递给_db.Messages.where(exp1); 问题是我只有 exp2,我需要将类型转换为 Message ,所有属性都相同!

现在我这样做了:

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

这个问题是输入参数被改变了是的!但是 lambda "x.mesID" 体内的 x 是旧类型。

有什么方法可以更改正文中的所有参数类型或更改输入参数也反映正文?

我想这是我在使用 LINQ 时一直遇到的一个大问题,因为在层之间我无法传递生成的类,因为这会使层耦合,所以我必须制作轻量级的类,现在我该如何使用类似的方法_db.Messages.where();从业务层?!虽然业务层对 Message 类型一无所知,但它只知道 MessageDTO。

【问题讨论】:

    标签: c# linq-to-sql lambda expression-trees


    【解决方案1】:

    不,基本上。表达式树是不可变的,并且包含完整的成员元数据(即 mesIDmessageDTO.mesID)。为此,您必须从头开始(通过访问者)重建表达式树,处理您需要支持的每种节点类型。

    如果表达式树是basic,这应该没问题,但是如果你需要支持整个色域呢?一个巨大的 PITA(尤其是在 .NET 4 中,它添加了更多的节点类型)。


    一个基本示例,只是完成示例所需的内容;您需要为更复杂的表达式添加更多节点类型:

    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; } }
    

    【讨论】:

    • via vistor 是什么意思?请问可以举个例子吗?
    • 访问者实现;即用于遍历整个树结构的一些代码构造,通常构造一个替代树(从叶节点回到根,因为每个分支都是不可变的)。这可能归结为一个巨大的开关(在节点类型上),每个节点类型都有递归处理。我会试着举一个例子......
    • 感谢 Marc 成功了,我只需要让它支持更多的 ExpressionType
    • @Stacker - 这很有趣;p 请注意,您应该能够重复使用大块;例如大多数二进制表达式应该能够使用现有的例子。
    • 只有一个问题要问你。你如何处理案例 ExpressionType.Convert:
    猜你喜欢
    • 1970-01-01
    • 2012-01-15
    • 1970-01-01
    • 2013-08-15
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 2011-06-03
    相关资源
    最近更新 更多