Comment créer une clause Expression AND à partir de deux expressions

c# expression-trees

Question

J'essaie de créer une clause where pour moi à l'aide de LINQ.

J'ai pu créer une colonne unique où clause et j'aimerais maintenant créer plusieurs colonnes où clauses.

J'ai vu du code à implémenter dans .Net 4 et versions ultérieures, mais comme je dois utiliser .Net 3.5, j'ai besoin d'un travail rapide pour y remédier. alors ce que j'essaie de faire, c'est ...

 Expression leftexp = {tag=>((tag.id=2)||(tag.id=3))}
 Expression rightexp = {tag=>((tag.uid="MU")||(tag.uid="ST"))}

à partir de ces deux expressions, je voudrais créer

 Expression leftexp = {tag=>((tag.id=2)||(tag.id=3))}
 Expression rightexp = {tag=>((tag.uid="MU")||(tag.uid="ST"))}

quelque chose comme ça que je pourrais passer à ma clause where dans LINQ.

J'ai essayé d'utiliser Expression.And (leftexp, rightexp)

mais j'ai eu l'erreur ..

L'opérateur binaire Et n'est pas défini pour les types
'System.Func 2[WebApplication1.View_MyView,System.Boolean]' and 'System.Func 2 [WebApplication1.View_MyView, System.Boolean]'.

Expression est nouveau pour moi et aurait peut-être examiné trop de code, ce qui rendrait un peu confus comment procéder. J'apprécierais vraiment si vous pouviez me diriger dans la bonne direction.

Réponse acceptée

Vous ne pouvez pas faire cela sans réécrire les deux arbres d’expression complets dans un nouvel arbre complet.

Raison: les objets expression-paramètre doivent être identiques pour l'ensemble de l'arborescence d'expression. Si vous combinez les deux, vous avez deux objets expression-paramètre pour le même paramètre, ce qui ne fonctionnera pas.

Il montre avec le code suivant:

Expression<Func<Tab, bool>> leftexp = tag => ((tag.id == 2) || (tag.id == 3));
Expression<Func<Tab, bool>> rightexp = tag => ((tag.uid == "MU") || (tag.uid == "ST"));

Expression binaryexp = Expression.AndAlso(leftexp.Body, rightexp.Body);
ParameterExpression[] parameters = new ParameterExpression[1] {
    Expression.Parameter(typeof(Tab), leftexp.Parameters.First().Name)
};
Expression<Func<Tab, bool>> lambdaExp = Expression.Lambda<Func<Tab, bool>>(binaryexp, parameters);

var lambda = lambdaExp.Compile();

Cela échoue lors de l'appel de lambdaExp.Compile (), qui génère l'exception suivante:

Expression<Func<Tab, bool>> leftexp = tag => ((tag.id == 2) || (tag.id == 3));
Expression<Func<Tab, bool>> rightexp = tag => ((tag.uid == "MU") || (tag.uid == "ST"));

Expression binaryexp = Expression.AndAlso(leftexp.Body, rightexp.Body);
ParameterExpression[] parameters = new ParameterExpression[1] {
    Expression.Parameter(typeof(Tab), leftexp.Parameters.First().Name)
};
Expression<Func<Tab, bool>> lambdaExp = Expression.Lambda<Func<Tab, bool>>(binaryexp, parameters);

var lambda = lambdaExp.Compile();

Ceci est dû au fait que, fondamentalement, je réutilise les expressions leftexp et rightexp, mais elles ont des expressions de paramètre différentes, les deux ne m’ayant pas été attribuées à Expression.Lambda<Func<Tab>>(...) appeler. Au fond de la leftexp et de la rightexp, il y a des objets expression de paramètre qui doivent correspondre à celui donné à l'appel Expression.Lambda<Func<Tab>>(...) .

Pour résoudre ce problème, vous devez recréer l'expression complète à l'aide d'une nouvelle expression de paramètre (unique) pour la balise de paramètre.

Voir ici pour plus d'informations sur le problème.


Réponse populaire

La réécriture des expressions a été facilitée par l’ajout d’ ExpressionVisitor à BCL. Avec quelques aides, la tâche devient presque triviale.

Voici une classe de visiteur que j'utilise pour appliquer un délégué aux nœuds d'arborescence:

internal sealed class ExpressionDelegateVisitor : ExpressionVisitor {

    private readonly Func<Expression , Expression> m_Visitor;
    private readonly bool m_Recursive;

    public static Expression Visit ( Expression exp , Func<Expression , Expression> visitor , bool recursive ) {
        return new ExpressionDelegateVisitor ( visitor , recursive ).Visit ( exp );
    }

    private ExpressionDelegateVisitor ( Func<Expression , Expression> visitor , bool recursive ) {
        if ( visitor == null ) throw new ArgumentNullException ( nameof(visitor) );
        m_Visitor = visitor;
        m_Recursive = recursive;
    }

    public override Expression Visit ( Expression node ) {
        if ( m_Recursive ) {
            return base.Visit ( m_Visitor ( node ) );
        }
        else {
            var visited = m_Visitor ( node );
            if ( visited == node ) return base.Visit ( visited );
            return visited;
        }
    }

}

Et voici les méthodes d'assistance pour simplifier la réécriture:

internal sealed class ExpressionDelegateVisitor : ExpressionVisitor {

    private readonly Func<Expression , Expression> m_Visitor;
    private readonly bool m_Recursive;

    public static Expression Visit ( Expression exp , Func<Expression , Expression> visitor , bool recursive ) {
        return new ExpressionDelegateVisitor ( visitor , recursive ).Visit ( exp );
    }

    private ExpressionDelegateVisitor ( Func<Expression , Expression> visitor , bool recursive ) {
        if ( visitor == null ) throw new ArgumentNullException ( nameof(visitor) );
        m_Visitor = visitor;
        m_Recursive = recursive;
    }

    public override Expression Visit ( Expression node ) {
        if ( m_Recursive ) {
            return base.Visit ( m_Visitor ( node ) );
        }
        else {
            var visited = m_Visitor ( node );
            if ( visited == node ) return base.Visit ( visited );
            return visited;
        }
    }

}

Ce qui permet de combiner les expressions comme celle-ci:

internal sealed class ExpressionDelegateVisitor : ExpressionVisitor {

    private readonly Func<Expression , Expression> m_Visitor;
    private readonly bool m_Recursive;

    public static Expression Visit ( Expression exp , Func<Expression , Expression> visitor , bool recursive ) {
        return new ExpressionDelegateVisitor ( visitor , recursive ).Visit ( exp );
    }

    private ExpressionDelegateVisitor ( Func<Expression , Expression> visitor , bool recursive ) {
        if ( visitor == null ) throw new ArgumentNullException ( nameof(visitor) );
        m_Visitor = visitor;
        m_Recursive = recursive;
    }

    public override Expression Visit ( Expression node ) {
        if ( m_Recursive ) {
            return base.Visit ( m_Visitor ( node ) );
        }
        else {
            var visited = m_Visitor ( node );
            if ( visited == node ) return base.Visit ( visited );
            return visited;
        }
    }

}

METTRE À JOUR:

Au cas où ExpressionVisitor ne soit pas disponible, sa source a été publiée il y a longtemps. Notre bibliothèque a utilisé cette implémentation jusqu'à la migration de .NET 4.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi