程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> LINQ那些事兒(4)- Query Expression和Query Operator

LINQ那些事兒(4)- Query Expression和Query Operator

編輯:關於.NET

我學習LINQ的時候是直接看MSDN和LINQ team的blog,經常會被裡面的一些名詞弄混,下面這些名詞你都弄懂了嗎?

Expression Tree

Expression

Lambda Expression

Query Expression

Query Operator

Expression Tree

和 Expression的區別類似XmlNode和XmlElement的區別。Expression Tree用於表達對IQueryable<T>類型數據源的查詢樹,是Select/Where/From等多個Query method嵌套,在運行時LINQ2SQL會根據Expression Tree來生成SQL語句。

Expression

確切的說是Expression類,為Expression Tree的每一個節點的基類,並提供了構造不同類型Expression的factory method。在System.Linq.Expression命名空間中提供了多種類型的Expression,經常用到的包括:

Class Description BinaryExpression 用來表達所有的二元運算,形式為(left) op (right)。如a+b, c && d等。 UnaryExpression 用來表達所有的一元運算,形式為op(operand)。如!a,b++等。 ConstantExpression 用來表達常量或外部變量(不在Expression Tree的控制結構內定義的變量)的定義。 ParameterExpression 用來表達Expression Tree控制結構內的局部變量的定義 MethodCallExpression 用來表達函數的調用 MemberExpression 用來表達屬性的訪問

讓我們用嘗試構造Expression Tree來表達

context.Customers.Where(c => c.City == “London”)

01 // context.Customers
02 ConstantExpression customersExpr = Expression.Constant(context.Customers);
03 // 定義Customer c
04 ParameterExpression parameterExpr = Expression.Parameter(typeof(Customer), "c");
05 // 訪問c.City屬性?
06 MemberExpression cityExpr = Expression.Property(parameterExpr, "City");
07 // c.City == "London"
08 BinaryExpression equalExpr = Expression.Equal(cityExpr, Expression.Constant("London"));
09 // c => c.City == "London"
10 LambdaExpression conditionExpr = Expression.Lambda(equalExpr, parameterExpr);
11
12 // 注意: Where方法的簽名是:
13 // Queryable.Where<T>(this IQueryable<T> source, Func<T, bool> predicate)
14 MethodCallExpression methodExpr = Expression.Call(
15 typeof(Queryable),
16      "Where",
17      new Type[] {typeof(Customer)},
18      customersExpr, conditionExpr
19 );
20
21 Console.WriteLine(methodExpr.ToString());
22 // 輸出:Table(Customer).Where(c => (c.City = "London"))

另外一個有趣的是,在Queryable的源代碼裡有不少Expression.Quote()的調用,把a變成’(a)是什麼意思? 來看看下面這段代碼,分別表示() => 2 + 3和 () => ‘(2 + 3):

1 BinaryExpression expr = Expression.Add(
2 Expression.Constant(2), Expression.Constant(3));
3 var expr1 = Expression.Lambda<Func<int>>(expr);
4 var expr2 = Expression.Lambda<Func<BinaryExpression>>(Expression.Quote(expr));
5  
6 Console.WriteLine(expr1.Compile()());  // 輸出:5
7 Console.WriteLine(expr2.Compile()());    // 輸出:(2 + 3)

Quote(A)的意思是,輸出值為A表達式,而不是A表達式的計算值。
Lambda Expression

lambda expression可以是Expression Tree的一個節點,可以用來創建一個委托。但你知道如何用lambda expression來創建節點和委托嗎?

01 LambdaExpression expr = () => 2 + 3;     // 錯誤
02 LambdaExpression expr = Expression.Lambda(
03 Expression.Add(Expression.Constant(2), Expression.Constant(3))) // 正確,定義節點
04
05 Expression<Func<int>> expr = () => 2 + 3;    //正確,定義節點
06 var expr = Expression.Lambda<Func<int>>(
07 Expression.Add(Expression.Constant(2), Expression.Constant(3))) // 正確,定義節點
08
09 Func<int> fn = () => 2 + 3;        // 正確,定義委托
10 Func<int> fn = () => { return 2 + 3; } //正確,定義委托

這裡問個問題,你認為下面的語句1和語句2會導致查詢操作有什麼區別(輸出結果都是6)?答案在“LINQ那些事(8)”。

1 var context = GenerateContext();
2 Expression<Func<Customer, bool>> condition = c => c.City == "London";
3
4 var result1 = context.Customers.Where(condition);               // 1
5 var result2 = context.Customers.Where(condition.Compile());     // 2
6
7 Console.WriteLine(result1.Count());
8 Console.WriteLine(result2.Count());

Query Operator

指的是在Enumerable和Queryable類中定義的用於用於對數據進行project/filter操作等的extension method,包括Where/Select/Join/OrderBy/GroupBy等。
Query Expression

剛接觸LINQ的時候感覺最特別就是可以用類sql的語句在csharp代碼裡寫查詢語句

1 var result =
2 from c in context.Customers
3 where c.City == "London"
4 select c;

VS對Query expression提供了諸多支持,包括intelligent sense和編譯檢查等等。但select和where在IL裡是沒有,只是compiler在運行時根據query expression轉化成對Query operator的調用。但是,並不是所有的query operator在query expression有等價表達,query expression支持的operator只有:

Select/SelectMany/Join/GroupJoin/Where/OrderBy/OrderByDescending/GroupBy/ThenBy/ThenByDescending。

既然query operator是通過extension method的方式提供,這就意味著我們可以通過自己編寫extension method,來改變query expression的行為,我們看下面的代碼:

01 public static class QueryOperatorExt
02     {
03         public static IQueryable<T> Where<T>(
04             this IQueryable<T> source, Expression<Func<T, bool>> predicate)
05         {
06             Console.WriteLine("Calling Where in the customer extension method");
07
08             return Queryable.Where(source, predicate);
09         }
10 }
11 class Program
12     {
13         static void Main(string[] args)
14         {
15             var context = GenerateContext();
16             var result =
17                 from c in context.Customers
18                 where c.City == "London"
19                 select c;
20
21             Console.WriteLine(result.Count());
22         }
23 }
24 // 輸出:
25 Calling Where in the customer extension method
26 6

總結:本節討論了Expression Tree/Expression/Lambda Expression/Query expression/Query Operator的含義和區別,示例了如何構建Expression Tree,正確使用lambda expression,以及如何通過extension method來擴展query expression。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved