2013-01-21 64 views
1

我有兩個不相關的類。一個暴露爲API,另一個由第三方API在內部使用。將LINQ表達式謂詞從一種類型更改爲另一種類型

實體從我們的API公開,而EntityProvider來自第三方程序集。

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

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

我們的API的消費者提供形式Expression <Func<Entity, bool>>的謂詞,我需要修改它Expression <Func<EntityProvider, bool>>,這樣我可以在同一傳遞給內部第三方組件。 請幫助完成此轉換。

回答

2

由於.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)); 
    }   
} 
相關問題