Share via


HOW TO:使用運算式樹狀結構建置動態查詢 (C# 和 Visual Basic)

在 LINQ 中,運算式樹狀架構可用來表示結構化查詢,而這些結構化查詢是以實作 IQueryable<T> 的資料來源做為目標。 例如,LINQ to SQL 提供者 (Provider) 會實作 IQueryable<T> 介面以查詢關聯式資料存放區。 C# 和 Visual Basic 編譯器 (Compiler) 都會將以這類資料來源做為目標的查詢,編譯成可在執行階段建置運算式樹狀架構的程式碼。 接著查詢提供者便可周遊運算式樹狀架構資料結構,並將它解譯成適用於該資料來源的查詢語言。

運算式樹狀架構也可在 LINQ 中使用,以代表指派給型別 Expression<TDelegate> 之變數的 Lambda 運算式。

本主題說明如何使用運算式樹狀架構來建立動態 (Dynamic) 的 LINQ 查詢。 如果在編譯時期不知道查詢的特定資訊,動態查詢就非常有用。 例如,應用程式提供的使用者介面可能可以讓使用者指定一個或多個用來篩選資料的述詞 (Predicate)。 為了使用 LINQ 進行查詢,這種應用程式必須在執行階段使用運算式樹狀架構來建立 LINQ 查詢。

範例

下列範例顯示如何使用運算式樹狀架構,針對 IQueryable 資料來源建構查詢,然後再執行該項查詢。 這個程式碼會建置 (Build) 運算式樹狀架構來表示下列查詢:

C# 查詢

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

Visual Basic 查詢

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

System.Linq.Expressions 命名空間 (Namespace) 中的 Factory 方法係用於建立表示構成整體查詢之運算式的運算式樹狀架構。 表示標準查詢運算式方法呼叫的運算式會參考這些方法的 Queryable 實作 (Implementation)。 最終的運算式樹狀架構則會傳遞至 IQueryable 資料來源提供者的 CreateQuery<TElement>(Expression) 實作,以建立型別 IQueryable 的可執行查詢。 取得結果的方式是列舉該查詢變數。

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

這個程式碼在傳遞至 Queryable.Where 方法的述詞中使用固定數目的運算式。 不過,您可以撰寫應用程式,用以合併數目隨使用者輸入而變動的述詞運算式。 您也可以根據使用者的輸入,改變查詢中呼叫的標準查詢運算子。

編譯程式碼

  • 在 Visual Studio 中建立新的 [主控台應用程式] 專案。

  • 將參考加入至 System.Core.dll (如果尚未參考)。

  • 包含 System.Linq.Expressions 命名空間 (Namespace)。

  • 從範例複製程式碼,並將它貼入 Main 方法 (C#) 或 Main Sub 程序 (Visual Basic) 中。

請參閱

工作

HOW TO:執行運算式樹狀結構 (C# 和 Visual Basic)

HOW TO:在執行階段動態指定述詞篩選條件 (C# 程式設計手冊)

概念

運算式樹狀架構 (C# 和 Visual Basic)

其他資源

LINQ 陣列種子:使用運算式樹狀架構視覺化檢視