Translating expression tree from a type to another type with complex mappings

后端 未结 2 621
借酒劲吻你
借酒劲吻你 2020-12-10 07:42

inspired by this answer I\'m trying to map a property on a model class to an expression based on the actual entity. These are the two classes involved:

publi         


        
2条回答
  •  醉话见心
    2020-12-10 08:31

    I took the liberty of modifying your code just a hair but this does the trick,

    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Id { get; set; }
        public DateTime? BirthDate { get; set; }
        public int CustomerTypeId { get; set; }
    }
    
    public class CustomerModel
    {
        public string FullName { get; set; }
        public bool HasEvenId { get; set; }
    }
    
    sealed class AToBConverter : ExpressionVisitor
    {
        private readonly Dictionary _parameters = new Dictionary();
        private readonly Dictionary _mappings;
    
        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (node.Type == typeof(TA))
            {
                ParameterExpression parameter;
                if (!this._parameters.TryGetValue(node, out parameter))
                {
                    this._parameters.Add(node, parameter = Expression.Parameter(typeof(TB), node.Name));
                }
                return parameter;
            }
            return node;
        }
    
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression == null || node.Expression.Type != typeof(TA))
            {
                return base.VisitMember(node);
            }
            Expression expression = this.Visit(node.Expression);
            if (expression.Type != typeof(TB))
            {
                throw new Exception("Whoops");
            }
            LambdaExpression lambdaExpression;
            if (this._mappings.TryGetValue(node.Member, out lambdaExpression))
            {
                return new SimpleExpressionReplacer(lambdaExpression.Parameters.Single(), expression).Visit(lambdaExpression.Body);
            }
            return Expression.Property(
                expression,
                node.Member.Name
            );
        }
    
        protected override Expression VisitLambda(Expression node)
        {
            return Expression.Lambda(
                this.Visit(node.Body),
                node.Parameters.Select(this.Visit).Cast()
            );
        }
    
        public AToBConverter(Dictionary mappings)
        {
            this._mappings = mappings;
        }
    }
    
    sealed class SimpleExpressionReplacer : ExpressionVisitor
    {
        private readonly Expression _replacement;
        private readonly Expression _toFind;
    
        public override Expression Visit(Expression node)
        {
            return node == this._toFind ? this._replacement : base.Visit(node);
        }
    
        public SimpleExpressionReplacer(Expression toFind, Expression replacement)
        {
            this._toFind = toFind;
            this._replacement = replacement;
        }
    }
    
    class Program 
    {
        private static Dictionary GetMappings()
        {
            var mappings = new Dictionary();
            var mapping = GetMappingFor(model => model.HasEvenId, customer => (customer.Id % 2) == 0);
            mappings.Add(mapping.Item1, mapping.Item2);
            mapping = GetMappingFor(model => model.FullName, customer => customer.FirstName + " " + customer.LastName);
            mappings.Add(mapping.Item1, mapping.Item2);
            return mappings;
        }
    
        private static Tuple GetMappingFor(Expression> fromExpression, Expression> toExpression)
        {
            return Tuple.Create(((MemberExpression)fromExpression.Body).Member, (LambdaExpression)toExpression);
        }
    
        static void Main()
        {
            Expression> source = model => model.HasEvenId && model.FullName == "John Smith";
            Expression> desiredResult = model => (model.Id % 2) == 0 && (model.FirstName + " " + model.LastName) == "John Smith";
            Expression output = new AToBConverter(GetMappings()).Visit(source);
            Console.WriteLine("The two expressions do {0}match.", desiredResult.ToString() == output.ToString() ? null : "not ");
            Console.ReadLine();
        }
    }
    

提交回复
热议问题