Конвертировать деревья выражений - PullRequest
10 голосов
/ 08 сентября 2010

да будет:

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

проблема в том, что входной параметр изменяется да! но х внутри тела лямбды "x.mesID" имеет старый тип.

Можно ли как-либо изменить все типы параметров в теле или изменить входной параметр, чтобы он тоже отражал тело?

Полагаю, это большая проблема, которую я всегда имею с LINQ, так как между слоями я не могу пропустить сгенерированные классы, так как это сделает слои связанными, поэтому мне придется создавать легковесные классы, теперь как мне использовать такой метод, как _db.Messages.where (); из слоя дела? !! в то время как слой busniess ничего не знает о типе сообщения, он знает только MessageDTO.

1 Ответ

11 голосов
/ 08 сентября 2010

Нет, в основном. Деревья выражений являются неизменными и содержат метаданные полных членов (то есть, что mesID равно messageDTO.mesID). Для этого вам нужно будет заново построить дерево выражений с нуля (через посетителя), обрабатывая каждый тип узла, который вам нужно поддерживать.

Если дерево выражений basic , все должно быть в порядке, но нужно ли поддерживать всю гамму? огромный PITA (особенно в .NET 4, который добавляет гораздо больше типов узлов).


A basic пример, который делает просто , что требуется для примера; вам нужно добавить больше типов узлов для более сложных выражений:

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