2011-03-25 5 views
11

Je tente de créer de manière dynamique des expressions basées sur un objet Specification.Remplacement du nom du paramètre dans le corps d'une expression

J'ai créé une classe ExpressionHelper qui a une expression privée comme ceci:

private Expression<Func<T, bool>> expression; 

public ExpressionHelper() 
{ 
    expression = (Expression<Func<T, bool>>)(a => true); 
} 

Et puis quelques méthodes faciles comme suit:

public void And(Expression<Func<T,bool>> exp); 

Je suis aux prises avec le corps du Et méthode Je veux fondamentalement déchirer le corps de exp, remplacer tous les paramètres avec ceux dans expression et l'ajouter ensuite à la fin du corps expression et AndAlso.

Je l'ai fait:

var newBody = Expression.And(expression.Body,exp.Body); 

expression = expression.Update(newBody, expression.Parameters); 

Mais qui finit avec mon expression qui ressemble à ceci:

{ a => e.IsActive && e.IsManaged } 

est-il un moyen plus simple de le faire? Ou comment puis-je déchirer ces e et les remplacer par un?

Répondre

20

La plus simple approche ici est Expression.Invoke, par exemple:

public static Expression<Func<T, bool>> AndAlso<T>(
    Expression<Func<T, bool>> x, Expression<Func<T, bool>> y) 
{ 
    return Expression.Lambda<Func<T, bool>>(
     Expression.AndAlso(x.Body, Expression.Invoke(y, x.Parameters)), 
     x.Parameters); 
} 

Cela fonctionne très bien pour LINQ-à-objets et LINQ to SQL, mais il est pas pris en charge par EF. Pour EF, vous devrez utiliser un visiteur pour réécrire l'arbre, malheureusement.

En utilisant le code de: Combining two lambda expressions in c#

public static Expression<Func<T, bool>> AndAlso<T>(
    Expression<Func<T, bool>> x, Expression<Func<T, bool>> y) 
{ 
    var newY = new ExpressionRewriter().Subst(y.Parameters[0], x.Parameters[0]).Inline().Apply(y.Body); 

    return Expression.Lambda<Func<T, bool>>(
     Expression.AndAlso(x.Body, newY), 
     x.Parameters); 
} 

Ou dans .NET 4.0, en utilisant ExpressionVisitor:

class ParameterVisitor : ExpressionVisitor 
{ 
    private readonly ReadOnlyCollection<ParameterExpression> from, to; 
    public ParameterVisitor(
     ReadOnlyCollection<ParameterExpression> from, 
     ReadOnlyCollection<ParameterExpression> to) 
    { 
     if(from == null) throw new ArgumentNullException("from"); 
     if(to == null) throw new ArgumentNullException("to"); 
     if(from.Count != to.Count) throw new InvalidOperationException(
      "Parameter lengths must match"); 
     this.from = from; 
     this.to = to; 
    } 
    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     for (int i = 0; i < from.Count; i++) 
     { 
      if (node == from[i]) return to[i]; 
     } 
     return node; 
    } 
} 
public static Expression<Func<T, bool>> AndAlso<T>(
     Expression<Func<T, bool>> x, Expression<Func<T, bool>> y) 
{ 
    var newY = new ParameterVisitor(y.Parameters, x.Parameters) 
       .VisitAndConvert(y.Body, "AndAlso"); 
    return Expression.Lambda<Func<T, bool>>(
     Expression.AndAlso(x.Body, newY), 
     x.Parameters); 
} 
+0

Zut, JUSTE a Invoke travail en mémoire et testait sur EF lorsque cela est arrivé. Merci. –

+0

@robert IIRC Invoke ne fonctionne pas sur EF, donc vous devrez utiliser la seconde version –

+0

Votre approche fonctionne mais maintenant j'ai votre ReWriter incroyablement complexe et non testé dans mon code. :(Voir à quel point il semble intelligent, je vais le garder jusqu'à ce que je le comprends! –

Questions connexes