Remplacement du nom du paramètre dans le corps d'une expression

c# expression-trees linq

Question

J'essaie de construire de manière dynamique des expressions basées sur un objet Specification.

J'ai créé une classe ExpressionHelper qui a une expression privée comme celle-ci:

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 me bats avec le corps de la méthode Et. Je veux fondamentalement extraire le corps de exp , remplacer tous les paramètres par ceux de l' expression , puis l'ajouter à la fin du corps de l' expression , comme AndAlso.

J'ai fait ça:

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

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

Mais cela finit par ressembler à mon expression:

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

Y a-t-il un moyen plus simple de faire cela? Ou comment puis-je déchirer ces e et les remplacer par un?

Réponse acceptée

La méthode la plus simple 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-to-Objects et LINQ-to-SQL, mais n'est pas pris en charge par EF. Pour EF, vous devrez malheureusement utiliser un visiteur pour réécrire l'arbre.

Utilisation du code de: Combinaison de deux expressions lambda en 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, à l'aide d' 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);
}


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow