Gewusst wie: Verwenden von Ausdrucksbaumstrukturen zum Erstellen dynamischer Abfragen (C# und Visual Basic)

In LINQ werden Ausdrucksbaumstrukturen verwendet, um strukturierte Abfragen darzustellen, die auf Quellen mit Daten abzielen, durch die IQueryable<T> implementiert wird.Beispielsweise implementiert der LINQ to SQL-Anbieter die IQueryable<T>-Schnittstelle zum Abfragen relationaler Datenspeicher.Die Compiler von C# und Visual Basic kompilieren Abfragen, die auf diese Datenquellen ausgerichtet sind, in Code, durch den zur Laufzeit eine Ausdrucksbaumstruktur generiert wird.Anschließend kann der Abfrageanbieter die Datenstruktur der Ausdrucksbaumstruktur durchlaufen und in eine für die Datenquelle geeignete Abfragesprache übersetzen.

Ausdrucksbaumstrukturen werden in LINQ auch zur Darstellung von lambda-Ausdrücken verwendet, die Variablen vom Typ Expression<TDelegate> zugewiesen werden.

In diesem Thema wird beschrieben, wie Ausdrucksbaumstrukturen verwendet werden, um dynamische LINQ-Abfragen zu erstellen.Dynamische Abfragen sind nützlich, wenn die Einzelheiten einer Abfrage zur Kompilierzeit nicht bekannt sind.Beispielsweise kann eine Anwendung über eine Benutzeroberfläche verfügen, in der der Endbenutzer eines oder mehrere Prädikate zum Filtern der Daten angeben kann.Damit LINQ für Abfragen verwendet werden kann, muss dieser Anwendungstyp Ausdrucksbaumstrukturen verwenden, um die LINQ-Abfrage zur Laufzeit zu erstellen.

Beispiel

Im folgenden Beispiel wird veranschaulicht, wie eine Abfrage für eine IQueryable-Datenquelle mithilfe von Ausdrucksbaumstrukturen erstellt und dann ausgeführt wird.Im Code wird eine Ausdrucksbaumstruktur erstellt, um die folgende Abfrage darzustellen:

C#-Abfrage

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

Visual Basic-Abfrage

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

Die Factorymethoden im System.Linq.Expressions-Namespace werden zum Erstellen von Ausdrucksbaumstrukturen verwendet, die die Ausdrücke darstellen, aus denen sich die gesamte Abfrage zusammensetzt.Die Ausdrücke, die die Aufrufe der Standardabfrageoperator-Methoden darstellen, verweisen auf die Queryable-Implementierungen dieser Methoden.Die endgültige Ausdrucksbaumstruktur wird an die CreateQuery<TElement>(Expression)-Implementierung des IQueryable-Datenquellenanbieters übergeben, um eine ausführbare Abfrage vom Typ IQueryable zu erstellen.Um Ergebnisse zu erhalten, wird diese Abfragevariable aufgelistet.

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

In diesem Code wird in dem an die Queryable.Where-Methode übergebenen Prädikat eine feste Anzahl von Ausdrücken verwendet.Sie können jedoch auch eine Anwendung programmieren, in der eine variable Anzahl von Prädikatausdrücken kombiniert wird, die von der Benutzereingabe abhängig ist.Außerdem können abhängig von der Benutzereingabe auch die in der Abfrage aufgerufenen Standardabfrageoperatoren variieren.

Kompilieren des Codes

  • Erstellen Sie ein neues Konsolenanwendungsprojekt in Visual Studio.

  • Fügen Sie einen Verweis auf System.Core.dll hinzu, wenn er noch nicht vorhanden ist.

  • Schließen Sie den System.Linq.Expressions-Namespace ein.

  • Kopieren Sie den Code aus dem Beispiel, und fügen Sie ihn in die Main-Methode (C#) oder die Main Sub-Prozedur (Visual Basic) ein.

Siehe auch

Aufgaben

Gewusst wie: Ausführen von Ausdrucksbaumstrukturen (C# und Visual Basic)

Gewusst wie: Dynamisches Festlegen von Prädikatfiltern zur Laufzeit (C#-Programmierhandbuch)

Konzepte

Ausdrucksbaumstrukturen (C# und Visual Basic)

Weitere Ressourcen

LINQ Farm Seed: Verwenden der Schnellansicht der Ausdrucksbaumstruktur