Comment : utiliser des arborescences d'expression pour générer des requêtes dynamiques (C# et Visual Basic)

Dans LINQ, les arborescences d'expression sont utilisées pour représenter des requêtes structurées ciblant des sources de données qui implémentent IQueryable<T>. Par exemple, le fournisseur LINQ to SQL implémente l'interface IQueryable<T> pour interroger des magasins de données relationnels. Les compilateurs C# et Visual Basic compilent les requêtes ciblant ces sources de données en du code qui génère une arborescence d'expression lors de l'exécution. Le fournisseur de requête peut parcourir ensuite la structure de données de l'arborescence de l'expression et la traduire dans un langage de requête approprié pour la source de données.

Les arborescences d'expression sont également utilisées dans LINQ pour représenter des expressions lambda assignées aux variables de type Expression<TDelegate>.

Cette rubrique décrit comment utiliser des arborescences d'expression pour créer des requêtes LINQ dynamiques. Les requêtes dynamiques sont utiles lorsque les caractéristiques d'une requête ne sont pas connues lors de la compilation. Par exemple, une application peut fournir une interface utilisateur qui permet à l'utilisateur final de spécifier un ou plusieurs prédicats pour filtrer les données. Pour permettre l'utilisation de LINQ pour l'interrogation, ce type d'application doit utiliser des arborescences d'expression pour créer la requête LINQ lors de l'exécution.

Exemple

L'exemple suivant vous montre comment utiliser des arborescences d'expression pour générer une requête sur une source de données IQueryable, puis l'exécuter. Le code génère une arborescence d'expression pour représenter la requête suivante :

Requête C#

companies.Where(company => (company.ToLower() == "coho winery" || company.Length > 16)).OrderBy(company => company)

Requête Visual Basic

companies.Where(Function(company) company.ToLower() = "coho winery" OrElse company.Length > 16).OrderBy(Function(company) company)

Les méthodes de fabrique de l'espace de noms System.Linq.Expressions sont utilisées pour créer des arborescences d'expression qui représentent les expressions composant la requête globale. Les expressions qui représentent des appels aux méthodes d'opérateur de requête standard font référence aux implémentations Queryable de ces méthodes. La dernière arborescence d'expression est passée à l'implémentation CreateQuery<TElement>(Expression) du fournisseur de la source de données IQueryable pour créer une requête exécutable de type IQueryable. Les résultats sont obtenus par l'énumération de cette variable de requête.

        Dim companies = 
            {"Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light", 
             "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works", 
             "Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders", 
             "Blue Yonder Airlines", "Trey Research", "The Phone Company", 
             "Wingtip Toys", "Lucerne Publishing", "Fourth Coffee"}

        ' The IQueryable data to query.
        Dim queryableData As IQueryable(Of String) = companies.AsQueryable()

        ' Compose the expression tree that represents the parameter to the predicate.
        Dim pe As ParameterExpression = Expression.Parameter(GetType(String), "company")

        ' ***** Where(Function(company) company.ToLower() = "coho winery" OrElse company.Length > 16) *****
        ' Create an expression tree that represents the expression: company.ToLower() = "coho winery".
        Dim left As Expression = Expression.Call(pe, GetType(String).GetMethod("ToLower", System.Type.EmptyTypes))
        Dim right As Expression = Expression.Constant("coho winery")
        Dim e1 As Expression = Expression.Equal(left, right)

        ' Create an expression tree that represents the expression: company.Length > 16.
        left = Expression.Property(pe, GetType(String).GetProperty("Length"))
        right = Expression.Constant(16, GetType(Integer))
        Dim e2 As Expression = Expression.GreaterThan(left, right)

        ' Combine the expressions to create an expression tree that represents the
        ' expression: company.ToLower() = "coho winery" OrElse company.Length > 16).
        Dim predicateBody As Expression = Expression.OrElse(e1, e2)

        ' Create an expression tree that represents the expression:
        ' queryableData.Where(Function(company) company.ToLower() = "coho winery" OrElse company.Length > 16)
        Dim whereCallExpression As MethodCallExpression = Expression.Call( 
                GetType(Queryable), 
                "Where", 
                New Type() {queryableData.ElementType}, 
                queryableData.Expression, 
                Expression.Lambda(Of Func(Of String, Boolean))(predicateBody, New ParameterExpression() {pe}))
        ' ***** End Where *****

        ' ***** OrderBy(Function(company) company) *****
        ' Create an expression tree that represents the expression:
        ' whereCallExpression.OrderBy(Function(company) company)
        Dim orderByCallExpression As MethodCallExpression = Expression.Call( 
                GetType(Queryable), 
                "OrderBy", 
                New Type() {queryableData.ElementType, queryableData.ElementType}, 
                whereCallExpression, 
                Expression.Lambda(Of Func(Of String, String))(pe, New ParameterExpression() {pe}))
        ' ***** End OrderBy *****

        ' Create an executable query from the expression tree.
        Dim results As IQueryable(Of String) = queryableData.Provider.CreateQuery(Of String)(orderByCallExpression)

        ' Enumerate the results.
        For Each company As String In results
            Console.WriteLine(company)
        Next

        ' This code produces the following output:
        '
        ' Blue Yonder Airlines
        ' City Power & Light
        ' Coho Winery
        ' Consolidated Messenger
        ' Graphic Design Institute
        ' Humongous Insurance
        ' Lucerne Publishing
        ' Northwind Traders
        ' The Phone Company
        ' Wide World Importers

            string[] companies = { "Consolidated Messenger", "Alpine Ski House", "Southridge Video", "City Power & Light",
                               "Coho Winery", "Wide World Importers", "Graphic Design Institute", "Adventure Works",
                               "Humongous Insurance", "Woodgrove Bank", "Margie's Travel", "Northwind Traders",
                               "Blue Yonder Airlines", "Trey Research", "The Phone Company",
                               "Wingtip Toys", "Lucerne Publishing", "Fourth Coffee" };

            // The IQueryable data to query.
            IQueryable<String> queryableData = companies.AsQueryable<string>();

            // Compose the expression tree that represents the parameter to the predicate.
            ParameterExpression pe = Expression.Parameter(typeof(string), "company");

            // ***** Where(company => (company.ToLower() == "coho winery" || company.Length > 16)) *****
            // Create an expression tree that represents the expression 'company.ToLower() == "coho winery"'.
            Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
            Expression right = Expression.Constant("coho winery");
            Expression e1 = Expression.Equal(left, right);

            // Create an expression tree that represents the expression 'company.Length > 16'.
            left = Expression.Property(pe, typeof(string).GetProperty("Length"));
            right = Expression.Constant(16, typeof(int));
            Expression e2 = Expression.GreaterThan(left, right);

            // Combine the expression trees to create an expression tree that represents the
            // expression '(company.ToLower() == "coho winery" || company.Length > 16)'.
            Expression predicateBody = Expression.OrElse(e1, e2);

            // Create an expression tree that represents the expression
            // 'queryableData.Where(company => (company.ToLower() == "coho winery" || company.Length > 16))'
            MethodCallExpression whereCallExpression = Expression.Call(
                typeof(Queryable),
                "Where",
                new Type[] { queryableData.ElementType },
                queryableData.Expression,
                Expression.Lambda<Func<string, bool>>(predicateBody, new ParameterExpression[] { pe }));
            // ***** End Where *****

            // ***** OrderBy(company => company) *****
            // Create an expression tree that represents the expression
            // 'whereCallExpression.OrderBy(company => company)'
            MethodCallExpression orderByCallExpression = Expression.Call(
                typeof(Queryable),
                "OrderBy",
                new Type[] { queryableData.ElementType, queryableData.ElementType },
                whereCallExpression,
                Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
            // ***** End OrderBy *****

            // Create an executable query from the expression tree.
            IQueryable<string> results = queryableData.Provider.CreateQuery<string>(orderByCallExpression);

            // Enumerate the results.
            foreach (string company in results)
                Console.WriteLine(company);

            /*  This code produces the following output:

                Blue Yonder Airlines
                City Power & Light
                Coho Winery
                Consolidated Messenger
                Graphic Design Institute
                Humongous Insurance
                Lucerne Publishing
                Northwind Traders
                The Phone Company
                Wide World Importers
            */

Ce code utilise un nombre fixe d'expressions dans le prédicat passé à la méthode Queryable.Where. Toutefois, vous pouvez écrire une application qui combine un nombre variable d'expressions de prédicat dépendant de l'entrée utilisateur. Vous pouvez également varier les opérateurs de requête standard appelés dans la requête, selon l'entrée effectuée par l'utilisateur.

Compilation du code

  • Créez un projet Application console dans Visual Studio.

  • Ajoutez une référence à System.Core.dll si elle n'est pas déjà référencée.

  • Incluez l'espace de noms System.Linq.Expressions.

  • Copiez le code à partir de l'exemple et collez-le dans la méthode Main (C#) ou dans la procédure Main Sub (Visual Basic).

Voir aussi

Tâches

Comment : exécuter des arborescences d'expression (C# et Visual Basic)

Comment : spécifier dynamiquement des filtres de prédicat au moment de l'exécution (Guide de programmation C#)

Concepts

Arborescences d'expression (C# et Visual Basic)

Autres ressources

Nouveautés LINQ : utilisation du visualiseur de l'arborescence de l'expression (page éventuellement en anglais)