How to convert an expression tree of one type to another type of expression?

If I have two almost identical classes, Animal and AnimalViewModel and the expression tree associated with the view model, how can I translate it to Animal ?

 public class Animal { public string Species { get; set; } public string Name { get; set; } public string Sound { get; set; } } public class AnimalViewModel : ViewModelBase { public string Species { get; set; } public string Name { get; set; } public string Sound { get; set; } } 

How can I translate Expression<Func<AnimalViewModel,bool>> to Expression<Func<Animal,bool>> ?

 public static Expression<Func<Animal,bool>> Translate (Expression<Func<AnimalViewModel,bool>> expression) { // What goes here? I assume I have to traverse the tree somehow. } 
+8
c # lambda expression-trees
source share
2 answers

Here is the visitor doing this job.

  • it makes a copy of the parameter (since we will need to create a new parameter and replace all the links of the old parameter with the new one)
  • he walks the .Body tree, substituting this parameter and switching any member access to the old type with a similar name in the new type
  • it reassembles lambda using the parameter we invented in the lesson

the code:

 class TypeChangeVisitor : ExpressionVisitor { private readonly Type from, to; private readonly Dictionary<Expression, Expression> substitutions; public TypeChangeVisitor(Type from, Type to, Dictionary<Expression, Expression> substitutions) { this.from = from; this.to = to; this.substitutions = substitutions; } public override Expression Visit(Expression node) { // general substitutions (for example, parameter swaps) Expression found; if(substitutions != null && substitutions.TryGetValue(node, out found)) { return found; } return base.Visit(node); } protected override Expression VisitMember(MemberExpression node) { // if we see x.Name on the old type, substitute for new type if (node.Member.DeclaringType == from) { return Expression.MakeMemberAccess(Visit(node.Expression), to.GetMember(node.Member.Name, node.Member.MemberType, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Single()); } return base.VisitMember(node); } } public class Program { public static void Main() { Expression<Func<AnimalViewModel, bool>> predicate = x => x.Name == "abc"; var switched = Translate<AnimalViewModel, Animal>(predicate); } public static Expression<Func<TTo, bool>> Translate<TFrom, TTo>(Expression<Func<TFrom, bool>> expression) { var param = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name); var subst = new Dictionary<Expression, Expression> { { expression.Parameters[0], param } }; var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst); return Expression.Lambda<Func<TTo, bool>>(visitor.Visit(expression.Body), param); } } 

Note that if you have x.Something.Name , you may need to be a little more careful, but this should be a smart way.

+8
source share

Have you tried Automapper for something like this?

0
source share

All Articles