2015-09-16 5 views
1

Я искал высокий уровень SO, чтобы найти решение для моей проблемы.Преобразование типов деревьев выражений

Я нашел несколько ответов для того, когда речь идет о простых выражениях, как

var exp1 Expression<Func<T, bool>> x => x.Name == "MyName" 

Но у меня возникают проблемы, когда выражения, как:

var exp1 Expression<Func<T, bool>> x => x.Category.Name == "Coupe" 

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

Любой, кто может помочь с некоторыми указателями? Вот что у меня до сих пор:

private class CustomVisitor<T> : ExpressionVisitor 
{ 
private readonly ParameterExpression mParameter; 

public CustomVisitor(ParameterExpression parameter) 
{ 
    mParameter = parameter; 
} 

//this method replaces original parameter with given in constructor 
protected override Expression VisitParameter(ParameterExpression node) 
{ 
    return mParameter; 
} 
private int counter = 0; 

/// <summary> 
/// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression" />. 
/// </summary> 
/// <param name="node">The expression to visit.</param> 
/// <returns> 
/// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression. 
/// </returns> 
/// <exception cref="System.NotImplementedException"></exception> 
protected override Expression VisitMember(MemberExpression node) 
{ 
    counter++; 
    System.Diagnostics.Debug.WriteLine("{0} - {1}", node.ToString(), counter); 
    try 
    { 
     //only properties are allowed if you use fields then you need to extend 
     // this method to handle them 
     if (node.Member.MemberType != System.Reflection.MemberTypes.Property) 
      throw new NotImplementedException(); 

     //name of a member referenced in original expression in your 
     //sample Id in mine Prop 
     var memberName = node.Member.Name; 
     //find property on type T (=PersonData) by name 
     var otherMember = typeof(T).GetProperty(memberName); 
     //visit left side of this expression p.Id this would be p 
     var inner = Visit(node.Expression); 

     return Expression.Property(inner, otherMember); 
    } 
    catch (Exception ex) 
    { 
     return null; 
    } 
} 
} 

метод Полезность:

public static Expression<Func<TDestin, T>> ConvertTypesInExpression<TSource, TDestin, T>(Expression<Func<TSource, T>> source) 
{ 
    var param = Expression.Parameter(typeof(TDestin)); 

    var body = new CustomVisitor<TDestin>(param).Visit(source.Body); 

    Expression<Func<TDestin, T>> lambda = Expression.Lambda<Func<TDestin, T>>(body, param); 

    return lambda; 
} 

И он используется так:

var changedFilter = ConvertTypesInExpression<ClientNotificationRuleDto, ClientNotificationRule, bool>(filterExpression); 

Так что, если кто-нибудь может помочь с некоторыми Ideias или указатели, это было бы здорово!

ответ

1

Анализ этого теста:

class Replaced 
{ 
    public Inner Inner { get; set; } 
} 

class Inner 
{ 
    public string Name { get; set; } 
} 

class Replacing 
{ 
    public Inner Inner { get; set; } 
} 

[TestClass] 
public class UnitTest1 
{ 
    [TestMethod] 
    public void TestMethod1() 
    { 
     var parameter = Expression.Parameter(typeof(Replacing)); 
     var visitor = new CustomVisitor(parameter); 
     Expression<Func<Replaced, bool>> expression = x => x.Inner.Name == "ss"; 
     var resultExpression = (Expression<Func<Replacing, bool>>)visitor.Visit(expression); 

     var function = resultExpression.Compile(); 
     var result = function(new Replacing 
     { 
      Inner = new Inner 
      { 
       Name = "ss" 
      } 
     }); 

     Assert.IsTrue(result); 
    } 
} 

internal class CustomVisitor : ExpressionVisitor 
{ 
    private readonly ParameterExpression mParameter; 

    private int counter = 0; 

    public CustomVisitor(ParameterExpression parameter) 
    { 
     mParameter = parameter; 
    } 

    protected override Expression VisitLambda<T>(Expression<T> node) 
    { 
     return Expression.Lambda(
      Visit(node.Body), 
      node.Parameters.Select(x => (ParameterExpression)Visit(x)).ToArray()); 
//or simpler but less generic   
//return Expression.Lambda(Visit(node.Body), mParameter); 
    } 

    //this method will be called twice first for Name and then for Inner 
    protected override Expression VisitMember(MemberExpression node) 
    { 
     counter++; 
     System.Diagnostics.Debug.WriteLine("{0} - {1}", node.ToString(), counter); 

     if (node.Member.MemberType != System.Reflection.MemberTypes.Property) 
      throw new NotImplementedException(); 

     var memberName = node.Member.Name; 
     var inner = Visit(node.Expression); 
     var otherMember = inner.Type.GetProperty(memberName); 
     return Expression.Property(inner, otherMember); 
    } 

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

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

PS: Никогда не улавливайте базовый класс Исключение из его просто плохой практики и возврат паники к исключению - это просто неправильно.

+0

Спасибо! Я проверю это. Об исключении, я знаю ... это не производственный код;) – Stargazer

+0

Спасибо! Используя ваш код, мне удалось заставить его работать. В моем первоначальном коде не было части VisitLambda, и это имело значение. Благодаря! P.S.- Мне нужно изучить больше этого дерева выражений. :) – Stargazer

+0

Ну, это все хорошо и отлично, пока ... Мне нужно иметь дело с захваченными переменными, что-то вроде: x => x.Name = varContainingName Я изо всех сил стараюсь расширить это, чтобы справиться с этим ... – Stargazer

-1

С драгоценной помощью @Rafal и идеи от this, мне удалось добиться решения для моих потребностей

public static class EXpressionTreeTools 
{ 
    #region ConvertTypesInExpression 

    /// <summary> 
    /// Converts the types in the expression. 
    /// </summary> 
    /// <typeparam name="TSource">The source type (the "replacee").</typeparam> 
    /// <typeparam name="TDestin">The destiny type (the replacer).</typeparam> 
    /// <typeparam name="T">The type of the result fo the expression evaluation</typeparam> 
    /// <param name="source">The source expression.</param> 
    /// <returns></returns> 
    public static Expression<Func<TDestin, T>> ConvertTypesInExpression<TSource, TDestin, T>(Expression<Func<TSource, T>> source) 
    { 
     var parameter = Expression.Parameter(typeof(TDestin)); 
     var visitor = new CustomVisitor(parameter); 
     //Expression<Func<TSource, bool>> expression = x => x.Inner.Name == "ss"; 
     Expression<Func<TDestin, T>> resultExpression = (Expression<Func<TDestin, T>>)visitor.Visit(source); 

     return resultExpression; 
    } 

    #endregion 

    #region CustomVisitor 

    /// <summary> 
    /// A custom "visitor" class to traverse expression trees 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    internal class CustomVisitor : ExpressionVisitor 
    { 
     private readonly ParameterExpression mParameter; 

     public CustomVisitor(ParameterExpression parameter) 
     { 
      mParameter = parameter; 
     } 

     protected override Expression VisitLambda<T>(Expression<T> node) 
     { 
      return Expression.Lambda(
       Visit(node.Body), 
       node.Parameters.Select(x => (ParameterExpression)Visit(x)).ToArray()); 
      //or simpler but less generic   
      //return Expression.Lambda(Visit(node.Body), mParameter); 
     } 

     //this method will be called twice first for Name and then for Inner 
     protected override Expression VisitMember(MemberExpression node) 
     { 
      if (node.Member.MemberType != System.Reflection.MemberTypes.Property) 
      //throw new NotImplementedException(); 
      { 
       Expression exp = this.Visit(node.Expression); 

       if (exp == null || exp is ConstantExpression) // null=static member 
       { 
        object @object = exp == null ? null : ((ConstantExpression)exp).Value; 
        object value = null; Type type = null; 
        if (node.Member is FieldInfo) 
        { 
         FieldInfo fi = (FieldInfo)node.Member; 
         value = fi.GetValue(@object); 
         type = fi.FieldType; 
        } 
        else if (node.Member is PropertyInfo) 
        { 
         PropertyInfo pi = (PropertyInfo)node.Member; 
         if (pi.GetIndexParameters().Length != 0) 
          throw new ArgumentException("cannot eliminate closure references to indexed properties"); 
         value = pi.GetValue(@object, null); 
         type = pi.PropertyType; 
        } 
        return Expression.Constant(value, type); 
       } 
       else // otherwise just pass it through 
       { 
        return Expression.MakeMemberAccess(exp, node.Member); 
       } 
      } 
      var memberName = node.Member.Name; 
      var inner = Visit(node.Expression); 
      var otherMember = inner.Type.GetProperty(memberName); 
      return Expression.Property(inner, otherMember); 
     } 

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

    #endregion 
} 
Смежные вопросы