2013-02-18 43 views
9

我有這樣的方法:如何更改表達式樹中的類型?

private bool Method_1(Expression<Func<IPerson, bool>> expression) 
{ 
    /* Some code that will call Method_2 */ 
} 

在這種方法我想改變IPerson類型爲另一種類型。我想打電話,看起來像這樣一種方法:

private bool Method_2(Expression<Func<PersonData, bool>> expression) 
{ 
    /* Some code */ 
} 

所以,在method_1我需要改變IPersonPersonData。我怎樣才能做到這一點?

編輯:

當我打電話:Method_1(p => p.Id == 1)我想「拯救」的條件(p.Id == 1),但我想在另一種類型,即IPerson執行這一條件。所以,我需要改變的表達或創建IPerson

編輯II一種新的表達:

對於那些有興趣誰,這就是(現在)我的解決方案:

private class CustomExpressionVisitor<T> : ExpressionVisitor 
{ 
    ParameterExpression _parameter; 

    public CustomExpressionVisitor(ParameterExpression parameter) 
    { 
     _parameter = parameter; 
    } 

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

    protected override Expression VisitMember(MemberExpression node) 
    { 
     if (node.Member.MemberType == System.Reflection.MemberTypes.Property) 
     { 
      MemberExpression memberExpression = null; 
      var memberName = node.Member.Name; 
      var otherMember = typeof(T).GetProperty(memberName); 
      memberExpression = Expression.Property(Visit(node.Expression), otherMember); 
      return memberExpression; 
     } 
     else 
     { 
      return base.VisitMember(node); 
     } 
    } 
} 

這是我使用它的方式:

public virtual bool Exists(Expression<Func<Dto, bool>> expression) 
{ 
    var param = Expression.Parameter(typeof(I)); 
    var result = new CustomExpressionVisitor<I>(param).Visit(expression.Body); 
    Expression<Func<I, bool>> lambda = Expression.Lambda<Func<I, bool>>(result, param); 

    bool exists = _repository.Exists(lambda); 
    return exists; 
} 
+1

'IPerson'和'PersonData'之間有關係嗎? – GolfWolf 2013-02-18 09:16:07

+0

@ w0lf不,他們不認識對方。 'PersonData'是接口的DTO版本('IPerson')。 – Martijn 2013-02-18 09:17:37

+0

我看不出如何做到這一點。你可以發表一個具體表達式如何看起來像的例子,如果你可以手動使用'PersonData',你會如何轉換它? – GolfWolf 2013-02-18 09:19:22

回答

15

如果使用.NET 4這是很容易(更新:如評論ExpressionVisitor注意到,在第4版沒有4.5加入),這將需要一些谷歌搜索舊框架:

有一些假設,但我認爲他們是有效的您的DTO和實體方案 - 訪問的屬性必須匹配。

class PersonData 
{ 
    public bool Prop { get; set; } 
} 

interface IPerson 
{ 
    bool Prop { get; set; } 
} 

在.NET 4中有定義ExpressionVisitor類,如果您使用舊的一個,那麼你需要編寫或找到實現它,使這個輕鬆了不少:

class Visitor<T> : ExpressionVisitor 
{ 
    ParameterExpression _parameter; 

    //there must be only one instance of parameter expression for each parameter 
    //there is one so one passed here 
    public Visitor(ParameterExpression parameter) 
    { 
     _parameter = parameter; 
    } 

    //this method replaces original parameter with given in constructor 
    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     return _parameter; 
    } 

    //this one is required because PersonData does not implement IPerson and it finds 
    //property in PersonData with the same name as the one referenced in expression 
    //and declared on IPerson 
    protected override Expression VisitMember(MemberExpression node) 
    { 
     //only properties are allowed if you use fields then you need to extend 
     // this method to handle them 
     if (node.Member.MemberType != System.Reflection.MemberTypes.Property) 
      throw new NotImplementedException(); 

     //name of a member referenced in original expression in your 
     //sample Id in mine Prop 
     var memberName = node.Member.Name; 
     //find property on type T (=PersonData) by name 
     var otherMember = typeof(T).GetProperty(memberName); 
     //visit left side of this expression p.Id this would be p 
     var inner = Visit(node.Expression); 
     return Expression.Property(inner, otherMember); 
    } 
} 

概念證明:

class Program 
{ 
    static void Main() 
    { 
     //sample expression 
     Expression<Func<IPerson, bool>> expression = x => x.Prop; 

     //parameter that will be used in generated expression 
     var param = Expression.Parameter(typeof(PersonData)); 
     //visiting body of original expression that gives us body of the new expression 
     var body = new Visitor<PersonData>(param).Visit(expression.Body); 
     //generating lambda expression form body and parameter 
     //notice that this is what you need to invoke the Method_2 
     Expression<Func<PersonData, bool>> lambda = Expression.Lambda<Func<PersonData, bool>>(body, param); 
     //compilation and execution of generated method just to prove that it works 
     var boolValue = lambda.Compile()(new PersonData()); 
    } 
} 

請注意,這將適用於簡單的表達式。如果您需要處理x.Prop.Prop1 < 3,則需要進一步擴展。

+0

謝謝。問是否可以在代碼中提供一些評論太多了?我發現很難理解,因爲表達(樹?)並不真的是我的專業領域...... – Martijn 2013-02-18 09:38:24

+1

@Martijn可以嗎? – Rafal 2013-02-18 09:50:41

+0

非常感謝!我幾乎不敢問,但你是否也可以提供評論主應用程序? – Martijn 2013-02-18 09:58:32