【问题标题】:Change a LINQ expression predicate from one type to another将 LINQ 表达式谓词从一种类型更改为另一种类型
【发布时间】:2017-11-23 20:47:20
【问题描述】:

我有两个不相关的课程。一种作为 API 公开,另一种由 3rd 方 API 在内部使用。

Entity 来自我们的 API,而 EntityProvider 来自第 3 方程序集。

class Entity
{
  public A { get; set; }
}

class EntityProvider
{
  public A { get; set; }
}

我们 API 的使用者将提供 Expression <Func<Entity, bool>> 形式的谓词,我需要将其修改为 Expression <Func<EntityProvider, bool>> 以便我可以将其传递给内部 3rd 方程序集。 请帮助完成此转换。

【问题讨论】:

    标签: linq entity-framework


    【解决方案1】:

    由于 .NET 中的表达式是不可变的,唯一的方法是重建整个表达式。为此,通常需要从 ExpressionVisitor 类继承。根据您必须转换的表达式的复杂性,这可能非常复杂。

    这是一个使用简单表达式(如 x=>x.Someproperty == somevalue )的访问者的简单示例。这只是一个让您入门的示例,它还没有完成或测试(例如,它不会处理表达式中的方法调用)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    //Type from which to convert
    public class A
    {
        public int Property1 { get; set; }
        public int Property2 { get; set; }
    }
    
    //Type to which we want the Expression converted
    public class B
    {
        public int Property1 { get; set; }
        public int Property2 { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            //the expression we want to convert expresion
            Expression<Func<A, bool>> expA = x => x.Property1 == 6 && x.Property2 == 3;
    
            var visitor = new ParameterTypeVisitor<A,B>(expA);
            var expB = visitor.Convert();
            var b = new B() { Property1 = 6, Property2 = 3 };
    
            //try the converted expression
            var result = expB.Compile().Invoke(b);
    
        }
    }    
    
    public class ParameterTypeVisitor<TFrom,TTo> : ExpressionVisitor
    {
    
        private Dictionary<string, ParameterExpression> convertedParameters;
        private Expression<Func<TFrom, bool>> expression;
    
        public ParameterTypeVisitor(Expression<Func<TFrom,bool>> expresionToConvert )
        {
            //for each parameter in the original expression creates a new parameter with the same name but with changed type 
            convertedParameters = expresionToConvert.Parameters
                .ToDictionary(
                    x => x.Name,
                    x => Expression.Parameter(typeof (TTo), x.Name)
                );
    
            expression = expresionToConvert;
        }
    
        public Expression<Func<TTo,bool>> Convert()
        {
            return (Expression<Func<TTo, bool>>)Visit(expression);
        }
    
        //handles Properties and Fields accessors 
        protected override Expression VisitMember(MemberExpression node)
        {
            //we want to replace only the nodes of type TFrom
            //so we can handle expressions of the form x=> x.Property.SubProperty
            //in the expression x=> x.Property1 == 6 && x.Property2 == 3
            //this replaces         ^^^^^^^^^^^         ^^^^^^^^^^^            
            if (node.Member.DeclaringType == typeof(TFrom))
            {
                //gets the memberinfo from type TTo that matches the member of type TFrom
                var memeberInfo = typeof (TTo).GetMember(node.Member.Name).First();
    
                //this will actually call the VisitParameter method in this class
                var newExp = Visit(node.Expression);
                return Expression.MakeMemberAccess(newExp, memeberInfo);
            }
            else 
            {
                return base.VisitMember(node);
            }
        }
    
        // this will be called where ever we have a reference to a parameter in the expression
        // for ex. in the expression x=> x.Property1 == 6 && x.Property2 == 3
        // this will be called twice     ^                   ^
        protected override Expression VisitParameter(ParameterExpression node)
        {            
            var newParameter = convertedParameters[node.Name];
            return newParameter;
        }
    
        //this will be the first Visit method to be called
        //since we're converting LamdaExpressions
        protected override Expression VisitLambda<T>(Expression<T> node)
        {
            //visit the body of the lambda, this will Traverse the ExpressionTree 
            //and recursively replace parts of the expression we for which we have matching Visit methods 
            var newExp = Visit(node.Body);
    
            //this will create the new expression            
            return Expression.Lambda(newExp,convertedParameters.Select(x=>x.Value));
        }        
    }
    

    【讨论】:

      猜你喜欢
      • 2011-02-17
      • 2011-05-30
      • 1970-01-01
      • 2018-06-04
      • 1970-01-01
      • 1970-01-01
      • 2012-05-21
      相关资源
      最近更新 更多