HOW TO:使用運算式樹狀架構建置動態查詢
更新:2007 年 11 月
本主題說明如何使用運算式樹狀架構來建立動態 (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 的可執行查詢。取得結果的方式是列舉該查詢變數。
Dim companies() As String = _
{"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
*/
這個程式碼在傳遞至 Queryable.Where 方法的述詞中使用固定數目的運算式。不過,您可以撰寫應用程式,用以合併數目隨使用者輸入而變動的述詞運算式。您也可以根據使用者的輸入,改變查詢中呼叫的標準查詢運算子。
編譯程式碼
在 Visual Studio 中建立新的 [主控台應用程式] 專案。
將參考加入至 System.Core.dll (如果尚未參考)。
包含 System.Linq.Expressions 命名空間。
從範例複製程式碼,並將它貼入 Main 方法 (C#) 或 MainSub 程序 (Visual Basic) 中。
請參閱
工作
HOW TO:在執行階段動態指定述詞篩選條件 (C# 程式設計手冊)