Condividi tramite


Procedura: utilizzare strutture ad albero dell'espressione per la compilazione di query dinamiche (C# e Visual Basic)

In LINQ le strutture ad albero dell'espressione vengono utilizzate per rappresentare le query strutturate destinate alle origini di dati che implementano IQueryable. Ad esempio, il provider LINQ to SQL implementa l'interfaccia IQueryable per l'esecuzione di query su archivi dati relazionali. I compilatori C# e Visual Basic compilano le query destinate a tali origini dati nel codice che compila una struttura ad albero dell'espressione in fase di esecuzione. Il provider della query può quindi utilizzare la struttura dei dati della struttura ad albero dell'espressione e convertirla in un linguaggio di query adatto all'origine dati.

Le strutture ad albero dell'espressione vengono utilizzate anche in LINQ per rappresentare le espressioni lambda assegnate alle variabili di tipo Expression.

In questo argomento viene illustrato come utilizzare le strutture ad albero dell'espressione per creare query LINQ dinamiche. Le query dinamiche sono utili quando le specifiche di una query non sono note in fase di compilazione. Ad esempio, un'applicazione potrebbe fornire un'interfaccia utente che consente all'utente finale di specificare uno o più predicati per filtrare i dati. Per utilizzare LINQ per l'esecuzione delle query, questo tipo di applicazione deve utilizzare le strutture ad albero dell'espressione per creare la query LINQ in fase di esecuzione.

Esempio

Nell'esempio seguente viene illustrato come utilizzare le strutture ad albero dell'espressione per costruire una query su un'origine dati IQueryable ed eseguirla. Il codice compila una struttura ad albero dell'espressione per rappresentare la query seguente:

Query in C#

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

Query in Visual Basic

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

I metodi factory nello spazio dei nomi System.Linq.Expressions vengono utilizzati per creare strutture ad albero dell'espressione che rappresentano le espressioni che costituiscono la query complessiva. Le espressioni che rappresentano le chiamate ai metodi degli operatori di query standard fanno riferimento alle implementazioni Queryable di questi metodi. La struttura ad albero dell'espressione finale viene passata all'implementazione CreateQuery``1(Expression) del provider dell'origine dati IQueryable per creare una query eseguibile di tipo IQueryable. I risultati vengono ottenuti enumerando quella variabile di query.

        ' Add an Imports statement for System.Linq.Expressions. 

        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
            // Add a using directive for System.Linq.Expressions. 

            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
            */

Questo codice utilizza un numero fisso di espressioni nel predicato passato al metodo Queryable.Where. Tuttavia, è possibile scrivere un'applicazione che combina un numero variabile di espressioni di predicato che dipende dall'input dell'utente. È inoltre possibile variare gli operatori di query standard chiamati nella query, a seconda dell'input dell'utente.

Compilazione del codice

  • Creare un nuovo progetto Applicazione console in Visual Studio.

  • Aggiungere un riferimento a System.Core.dll, se non è già presente un riferimento.

  • Includere lo spazio dei nomi System.Linq.Expressions.

  • Copiare il codice dall'esempio e incollarlo nel metodo Main (C#) o nella procedura Main Sub (Visual Basic).

Vedere anche

Attività

Procedura: eseguire alberi delle espressioni (C# e Visual Basic)

Procedura: specificare dinamicamente i filtri dei predicati in fase di esecuzione (Guida per programmatori C#)

Concetti

Strutture ad albero dell'espressione (C# e Visual Basic)

Altre risorse

LINQ farm seed: Utilizzando il visualizzatore della struttura ad albero di l