将表达式树转换

12

让它存在:

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"还是旧类型。

有没有办法在lambda体内也修改所有参数类型,或者以某种方式更改输入参数以反映在lambda体内呢?

我想这是我在使用LINQ时经常遇到的一个大问题,因为我无法在不同层之间传递生成的类,否则会使层耦合,所以我必须使用轻量级的类。现在,业务层如何使用像_db.Messages.where()之类的方法呢?而业务层不知道Message类型,只知道MessageDTO。

1个回答

14
不行。表达式树是不可变的,并包含完整的成员元数据(即 mesIDmessageDTO.mesID)。要实现这一点,您需要从头开始重新构建表达式树(通过访问者),处理每个需要支持的节点类型。如果表达式树是基本的,那么这应该没问题,但如果您需要支持所有节点类型,那将非常麻烦(特别是在 .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 visitor)”?你能给我一个例子吗? - Stacker
一个访问者实现;即一些代码结构,您可以使用它来遍历整个树形结构,通常构建一棵替代树(从叶节点返回到根节点,因为每个分支都是不可变的)。这可能会归结为一个巨大的开关(在节点类型上),对于每个节点类型进行递归处理。我会尝试举一个例子... - Marc Gravell
1
@Stacker - 那是有趣的部分 ;p 请注意,您应该能够重复使用大块内容;例如,大多数二进制表达式应该能够使用现有示例。 - Marc Gravell
我有一个问题想问你。你如何处理ExpressionType.Convert的情况? - Stacker
@Stacker问道:“如何处理ExpressionType.Convert的情况?” 这个写法会抛出异常。 - Meester Over
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,