2013-02-18 17 views
9

Ho un metodo come questo:Come modificare un tipo in un albero di espressioni?

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

In questo metodo voglio cambiare il tipo di IPerson ad un altro tipo. Voglio chiamare un altro metodo che assomiglia a questo:

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

Così, in method_1 ho bisogno di cambiare IPerson-PersonData. Come posso fare questo?

Edit:

Quando chiamo: Method_1(p => p.Id == 1) voglio 'salvare' la condizione (p.Id == 1) ma voglio eseguire questa condizione su un altro tipo, vale a dire IPerson. Così, ho bisogno di alterare l'espressione o di creare una nuova espressione con IPerson

EDIT II:

Per coloro che sono interessati, questo è (per ora) la mia soluzione:

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); 
     } 
    } 
} 

E questo è il modo in cui lo uso:

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

esiste una relazione tra 'IPerson' e' PersonData'? – GolfWolf

+0

@ w0lf No, non si conoscono. 'PersonData' è la versione DTO dell'interfaccia (' IPerson'). – Martijn

+0

Non vedo come ciò potrebbe essere fatto. Puoi pubblicare un esempio di come dovrebbe essere un'espressione concreta e come lo trasformeresti per usare 'PersonData' se potessi farlo manualmente? – GolfWolf

risposta

15

E 'facile se si utilizza .NET 4 (aggiornamento: come indicato nel commento ExpressionVisitor è stato aggiunto nella versione 4 non 4.5) che richiederebbe alcuni googling per framework precedenti:

Esistono alcune ipotesi, ma penso che siano valide per lo scenario DTO e Entity: le proprietà cui si accede devono corrispondere.

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

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

NET 4 c'è ExpressionVisitor classe definita che rende questo molto più facile se si utilizza più vecchio, allora avete bisogno di scrivere o trovare realizzazione di esso:

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); 
    } 
} 

Proof of concept:

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()); 
    } 
} 

Si noti che questo funzionerà per le espressioni semplici. Se è necessario gestire x.Prop.Prop1 < 3, è necessario estendere ulteriormente.

+0

Grazie. E 'chiedere troppo se potresti fornire qualche commento nel codice? Trovo difficile da capire poiché l'espressione (alberi?) Non è davvero il mio campo di competenza ... – Martijn

+1

@Martijn è OK? – Rafal

+0

Grazie mille! Quasi non oso chiederlo, ma potresti anche fornire l'app principale con commenti? – Martijn

Problemi correlati