2015-05-29 5 views
4

У меня есть два класса:Как правильно заменить тип выражения?

public class DalMembershipUser 
{ 
     public string UserName { get; set; } 
     //other members 
} 

public class MembershipUser 
{ 
     public string UserName { get; set; } 
     //other members 
} 

У меня есть функция:

public IEnumerable<DalMembershipUser> GetMany(Expression<Func<DalMembershipUser, bool>> predicate) 
    { 
     //but here i can use only Func<MembershipUser, bool> 
     //so i made transformation 
     query = query.Where(ExpressionTransformer<DalMembershipUser,MembershipUser>.Tranform(predicate)); 
} 

Текущая реализация:

public static class ExpressionTransformer<TFrom, TTo> 
    { 
     public class Visitor : ExpressionVisitor 
     { 
      private ParameterExpression _targetParameterExpression; 

      public Visitor(ParameterExpression parameter) 
      { 
       _targetParameterExpression = parameter; 
      } 

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

     public static Expression<Func<TTo, bool>> Tranform(Expression<Func<TFrom, bool>> expression) 
     { 
      ParameterExpression parameter = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name); 
      Expression body = expression.Body; 
      new Visitor(parameter).Visit(expression.Body); 
      return Expression.Lambda<Func<TTo, bool>>(body, parameter); 
     } 
    } 

// Где: .GetMany(u => u.UserName == "username");

Исключение: Свойство «System. String UserName 'не определен для типа' Memb ershipUser '
в строке: новый посетитель (параметр) .Visit (выражение.Body);

+0

Где находится 'запрос' во втором блоке? –

+0

Я отредактировал ваш заголовок. Пожалуйста, смотрите: «Если вопросы включают« теги »в их названиях?] (Http://meta.stackexchange.com/questions/19190/), где консенсус« нет, они не должны ». –

+0

Просто любопытно. Какова цель всего этого? – Kryptos

ответ

0

Наконец он работает. Но до сих пор не понимаю, почему создание четких параметров: Expression.Parameter(typeof(TTo), from.Parameters[i].Name); не работает и нужно извлечь.

public static class ExpressionHelper 
    { 

     public static Expression<Func<TTo, bool>> TypeConvert<TFrom, TTo>(
      this Expression<Func<TFrom, bool>> from) 
     { 
      if (from == null) return null; 

      return ConvertImpl<Func<TFrom, bool>, Func<TTo, bool>>(from); 
     } 

     private static Expression<TTo> ConvertImpl<TFrom, TTo>(Expression<TFrom> from) 
      where TFrom : class 
      where TTo : class 
     { 
      // figure out which types are different in the function-signature 

      var fromTypes = from.Type.GetGenericArguments(); 
      var toTypes = typeof(TTo).GetGenericArguments(); 

      if (fromTypes.Length != toTypes.Length) 
       throw new NotSupportedException("Incompatible lambda function-type signatures"); 

      Dictionary<Type, Type> typeMap = new Dictionary<Type, Type>(); 
      for (int i = 0; i < fromTypes.Length; i++) 
      { 
       if (fromTypes[i] != toTypes[i]) 
        typeMap[fromTypes[i]] = toTypes[i]; 
      } 

      // re-map all parameters that involve different types 
      Dictionary<Expression, Expression> parameterMap = new Dictionary<Expression, Expression>(); 
      ParameterExpression[] newParams = GenerateParameterMap<TFrom>(from, typeMap, parameterMap); 

      // rebuild the lambda 
      var body = new TypeConversionVisitor<TTo>(parameterMap).Visit(from.Body); 
      return Expression.Lambda<TTo>(body, newParams); 
     } 

     private static ParameterExpression[] GenerateParameterMap<TFrom>(
      Expression<TFrom> from, 
      Dictionary<Type, Type> typeMap, 
      Dictionary<Expression, Expression> parameterMap 
     ) 
      where TFrom : class 
     { 
      var newParams = new ParameterExpression[from.Parameters.Count]; 

      for (int i = 0; i < newParams.Length; i++) 
      { 
       Type newType; 
       if (typeMap.TryGetValue(from.Parameters[i].Type, out newType)) 
       { 
        parameterMap[from.Parameters[i]] = newParams[i] = Expression.Parameter(newType, from.Parameters[i].Name); 
       } 
      } 
      return newParams; 
     } 


     class TypeConversionVisitor<T> : ExpressionVisitor 
     { 
      private readonly Dictionary<Expression, Expression> parameterMap; 

      public TypeConversionVisitor(Dictionary<Expression, Expression> parameterMap) 
      { 
       this.parameterMap = parameterMap; 
      } 

      protected override Expression VisitParameter(ParameterExpression node) 
      { 
       // re-map the parameter 
       Expression found; 
       if (!parameterMap.TryGetValue(node, out found)) 
        found = base.VisitParameter(node); 
       return found; 
      } 
      public override Expression Visit(Expression node) 
      { 
       LambdaExpression lambda = node as LambdaExpression; 
       if (lambda != null && !parameterMap.ContainsKey(lambda.Parameters.First())) 
       { 
        return new TypeConversionVisitor<T>(parameterMap).Visit(lambda.Body); 
       } 
       return base.Visit(node); 
      } 

      protected override Expression VisitMember(MemberExpression node) 
      { 
       // re-perform any member-binding 
       var expr = Visit(node.Expression); 
       if (expr.Type != node.Type) 
       { 
        if (expr.Type.GetMember(node.Member.Name).Any()) 
        { 
         MemberInfo newMember = expr.Type.GetMember(node.Member.Name).Single(); 
         return Expression.MakeMemberAccess(expr, newMember); 
        } 
       } 
       return base.VisitMember(node); 
      } 
     } 
    } 
0

Вам нужно использовать выражение результата, возвращаемое методом Visit.

Просто изменение:

Expression body = expression.Body; 
new Visitor(parameter).Visit(expression.Body); 

по

Expression body = new Visitor(parameter).Visit(expression.Body); 
+0

ничего не изменилось; –