Как преобразовать дерево выражений? - PullRequest
1 голос
/ 27 февраля 2011

UPDATE

Я попытаюсь объяснить, что я имею в виду. Существует 2 различных классов (MyClass1 и MyClass2) и метод для преобразования class1 в class2:

class MyClass1
  {
        //...Some fields and properties
 }

  class MyClass2
   {
        //...Some fields and properties
  }

public MyClass2 Convert(MyClass1 class1)
{
//.....
return class2Object;
}

Есть 2 разных метода:

   void method1(Expression<Func<MyClass1, bool>> where, //other parameters)
    {
        //some operations
        //...............

        //need to call method2(Expression<Func<MyClass2, bool>>)
        //   BUT! How do I convert Expression<Func<MyClass1, bool>> 
        //   to Expression<Func<MyClass2, bool>>
    }

    void method2(Expression<Func<MyClass2, bool>> where, //other parameters)
    {
        //some operations
    }

Как преобразовать выражение MyClass1 , bool >> в выражение MyClass2 , bool>>

Ответы [ 4 ]

6 голосов
/ 27 февраля 2011

Позвольте мне угадать, что вы спрашиваете: ваши MyClass1 и MyClass2 выглядят одинаково (они оба имеют int field1 и string field2). Теперь у вас есть Expression<Func<MyClass1,bool>>, что-то вроде:

Expression<Func<MyClass1, bool>> exp1 = x => x.field1 == 100; // x is MyClass1

И вам нужно другое выражение, которое выглядит так же , но оно для MyClass2:

Expression<Func<MyClass2, bool>> exp2 = x => x.field1 == 100; // x is MyClass2

Если это то, что вы спрашиваете, вот мой ответ:

Чтобы получить выражение для MyClass2, вам необходимо заменить все x в exp1, потому что все x в exp1 имеют тип MyClass1. ExpressionVisitor это именно то, что вы хотите.

class MyExpressionVisitor : ExpressionVisitor
{
    public ParameterExpression NewParameterExp { get; private set; }

    public MyExpressionVisitor(ParameterExpression newParameterExp)
    {
        NewParameterExp = newParameterExp;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return NewParameterExp;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Member.DeclaringType == typeof(MyClass1))
            return Expression.MakeMemberAccess(this.Visit(node.Expression), 
               typeof(MyClass2).GetMember(node.Member.Name).FirstOrDefault());
        return base.VisitMember(node);
    }
}

Посетитель пройдет (скажем «посещение») всего выражения, посетит все узлы. Когда дело доходит до узла ParameterExpression, мы меняем узел (поскольку это MyClass1, мы меняем его на MyClass2, см. Метод VisitParameter). Еще одна вещь, которую нам нужно изменить, - когда посетитель приходит к узлу, подобному x.field1, он посещает поле 1 в MyClass1, мы также должны изменить его (см. VisitMember). После прохождения всего exp1 мы получаем совершенно новый exp2 с заменой некоторых узлов, вот что мы хотим.

Expression<Func<MyClass1, bool>> exp1 = x => x.field1 == 100;

var visitor = new MyExpressionVisitor(Expression.Parameter(typeof(MyClass2), 
                        exp1.Parameters[0].Name));

var exp2 = Expression.Lambda<Func<MyClass2, bool>>
                (visitor.Visit(exp1.Body), visitor.NewParameterExp);

//the following is for testing
var data = new MyClass2();
Console.WriteLine(exp2.Compile()(data));  //False
data.field1 = 100;
Console.WriteLine(exp2.Compile()(data));   //True
1 голос
/ 27 февраля 2011

Деревья выражений являются неизменяемыми, поэтому для этого вам нужно будет пройтись по всему дереву, перестроив его и заменив любое использование типа на эквивалентное - обычно написав «посетитель».При обнаружении MemberExpression или MethodCallExpression вы должны проверить тип объявления члена - если это тот, который вам не нужен, воссоздать его (здесь полезно Expression.PropertyOrField).

Обратите внимание, что вы не можете сделатьэто только в местах, где это используется;все дерево должно быть восстановлено.Сейчас я не за компьютером, но если вы хотите, я могу сделать пример позже;оставьте комментарий, если вам нужен этот пример.

Обратите внимание, что это несколько усложняется несоответствием int / long и char / string.

0 голосов
/ 02 сентября 2012

Вы можете скомпилировать первое выражение для делегата, а затем преобразовать его с помощью
NJection.LambdaConverter , который является библиотекой, преобразующей делегаты в деревья выражений.

0 голосов
/ 28 февраля 2011
public CategoryViewModel GetSingle( Expression<Func<CategoryViewModel, bool>> where)
        {
            Expression<Func<DAL.EntityModels.Category, CategoryViewModel>> converter =
                c => ToBll(c);

            var param = Expression.Parameter(typeof(DAL.EntityModels.Category), "category");
            var body = Expression.Invoke(where, Expression.Invoke(converter, param));
            var lambda = Expression.Lambda<Func<DAL.EntityModels.Category, bool>>(body, param);

            return  (CategoryViewModel )_categoryRepository.GetSingle(lambda);

        }

//..............
public T GetSingle(Expression<Func<T, bool>> where)
        {
            return this.ObjectSet.Where(where).FirstOrDefault<T>();
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...