閱讀目錄:
- 1.動態LINQ查詢(動態構建Expression<T>表達式樹)
- 2.DLR動態語言運行時(基於CLR之上的動態語言運行時)
1】.動態LINQ查詢(動態構建Expression<T>表達式樹)
什么是動態LINQ查詢?LINQ的編寫是靜態的,因為C#是基於靜態類型系統原理設計的,在編寫時已經確定類型,也就是在編譯時就已經知道將要執行什么樣的查詢,條件是什么、排序方式是什么等等。那么很大一部分應用場合中我們需要根據用戶的選擇來查詢數據源,以往我們都是通過判斷的方式來拼接查詢的SQL字符串,但是現在我們面對是強類型的LINQ查詢,是否可以很方便的進行類似查詢。其實也沒有什么好神秘的,基本的實現原理是通過動態的構建表達式樹來實現IQueryable<T>接口的查詢。
其實動態LINQ查詢所能執行的最關鍵的因素在於Expression<T>對象是可以被動態編譯成可以執行的委托對象,委托對象是完全可以被直接使用的可執行代碼段,這就為動態LINQ查詢提供了基礎。對於IEnumerable<T>類型的查詢表達式方法都知道它的執行是不會直接接受Expression<T>類型對象的,那么動態LINQ是否能工作於IEnumerable<T>接口?其實可以的,有個很隱蔽的竅門隱藏在IQueryable<T>擴展方法對象Queryable中,也就是AsQueryable<T>方法,它返回的是一個實現了IQueryable<T>接口的EnumerableQuery對象,該對象的實現內容不是很復雜,將動態拼接的數據結構Expression<T>對象編譯成可以執行的匿名函數,然后直接執行查詢。
我們來看一下EnumerableQuery對象的重點,它肯定有一個地方是將Expression<T>對象Compiler的地方。
View Code
1 private IEnumerator<T> GetEnumerator() 2 { 3 if (this.enumerable == null) 4 { 5 EnumerableRewriter rewriter = new EnumerableRewriter(); 6 Expression<Func<IEnumerable<T>>> expression2 = 7 Expression.Lambda<Func<IEnumerable<T>>>(rewriter.Visit(this.expression), (IEnumerable<ParameterExpression>)null); 8 this.enumerable = expression2.Compile()(); //(1)重點 9 } 10 return this.enumerable.GetEnumerator(); 11 }
在上述代碼中的“(1)重點”的地方,我們很清楚的看見表達式樹被動態編譯后然后緊接着又被執行,這里就能看出為什么IEnumerable<T>對象需要能夠被轉換成IQueryable<T>對象。這樣就可以消除IEnumerable<T>、IQueryable<T>這兩個接口之間的動態查詢瓶頸。
為什么需要動態LINQ查詢,上面說過問題出在我們沒辦法在運行時再去編寫Lambda表達式了,都知道Lambda表達式到最后就是被編譯成Expression表達式樹對象,所以我們可以在運行時自己動態的構建Expression對象,這樣就可以將動態構建出來的表達式樹對象直接傳入到需要的方法中。如果查詢的數據對象是IEnumerable<T>則會被動態編譯成可以執行的委托然后直接執行,如果查詢的是IQueryable<T>則順其自然的被提供程序解析執行。
下面我們來看一個簡單的動態查詢例子:
View Code
1 //構造Student數組 2 Student[] StudentArrary = new Student[3] 3 { 4 new Student(){Name="王清培", Age=24, Sex="男", Address="江蘇南京"}, 5 new Student(){Name="陳玉和", Age=23, Sex="女", Address="江蘇鹽城"}, 6 new Student(){Name="金源", Age=22, Sex="女", Address="江蘇淮安"} 7 };
這是一組數據,為了簡單測試就不搞那么麻煩的Linq to Sql數據源了。我們將要通過動態的構建表達式樹來做為查詢的邏輯,以往我們的Lambda在這個時候派不上用場了,在運行時我們無法再去構建委托類型。
現在的需求是從界面上接受一個Name值的輸入,LINQ的查詢只需要直接寫就行了。
View Code
1 var list = from i in StudentArrary where i.Name == "王清培" select i;
但是我們需要動態的構建表達式樹來執行查詢,表達式樹的任何一個節點都有相對應的Expression派生類型,所以我們只要將相關類型組裝起來就行了。由於我建的示例程序的類型是控制台程序,所以我們就用簡短的方式演示一下如何構建表達式樹。
View Code
1 ParameterExpression parameter = Expression.Parameter(typeof(Student), "stu");//表示二元運算符的左邊參數名稱 2 //表示"stu"參數的"stu.Name"中的Name屬性,Name屬性必須是反射獲取的元數據才行,這樣框架就才可以找到它 3 MemberExpression property = Expression.MakeMemberAccess(parameter, typeof(Student).GetMember("Name")[0]); 4 //表示常量值 5 Console.WriteLine("請輸入要查詢人的名稱:"); 6 ConstantExpression name = Expression.Constant(Console.ReadLine());//從用戶輸入流中讀取值 7 BinaryExpression binary = Expression.MakeBinary(ExpressionType.Equal, property, name);//拼接==運算符的左邊、右邊 8 //完整的表達式是Lambda才對 9 LambdaExpression lambda = Expression.Lambda(binary, parameter); 10 //重要的就在這里,我們將完整的Lambda表達式直接變成可以執行的委托 11 Func<Student, bool> wheredelegate = lambda.Compile() as Func<Student, bool>; 12 //將編譯后的可執行委托直接放入Where方法中執行 13 var list2 = StudentArrary.AsQueryable<Student>().Where(wheredelegate); 14 foreach (var i in list2) 15 { 16 Console.WriteLine("查詢列表:"); 17 Console.WriteLine("姓名:{0},年齡:{1},地址:{2}", i.Name, i.Age, i.Address); 18 } 19 Console.ReadLine();
圖例:

該例子的重點是如何動態構建邏輯,根據不同的項目要求完全可以將類似的功能封裝起來供以后重復使用。如果覺得手動編寫表達式樹很麻煩的話,建議可以找一個輔助類能將Lambda表達式的對象樹都能打印出來的工具,然后對着這棵樹在去寫就簡單多了。
關於動態LINQ的第三方的API不是很多,比較常用的就是Dynamic.cs的使用,具體我沒有用過,看過相關文檔應該還是比較方便的。它的內部原理其實還是動態的構建表達式樹,只不過這部分工作被人家做了,而我們使用起來卻簡單的很多。
2】.DLR動態語言運行時(基於CLR之上的動態語言運行時)
從C#1一路走來,它變的越來越強大,.NET平台變得無所不能。很多人還一直咬着.NET不能跨平台,不能支持動態對象,不支持非托管等等理由來排斥它,然而他們所不知的是.NET已經悄無聲息的做出來一大舉動,那就是在靜態語言運行時上嵌入動態語言運行時環境。我想不是微軟不能支持所謂的缺點,而是它確實有它的本意。
動態語言運行時是在.NET4.0中引入的建立在CLR之上的運行時環境,目的是為了在靜態語言中能夠借鑒動態語言運行時的優點,比如強大的類型隨意變換,這點在設計應用開發框架時尤其重要,任何一個好的特性都需要大面積的使用模式才能變的更完美。
說到動態運行時就不得不提JS中讓人興奮的var定義的對象特性了,如果沒有留意在設計框架時而存在的煩惱其實很難發現動態運行和靜態語言之間的好與不好。很明顯的例子就是當我們定義一個數據類型的對象時,無法再在后期運行時對它進行其他類型的使用,看一個簡單的例子:
View Code
1 dynamic obj = 1;//整形 2 obj = "1";//字符串 3 obj = new { Name = "王清培", Age = 24, Address = "江蘇" };//匿名對象類型
在運行時我們可以隨意的設計對象的類型,我大膽的假設完全可以用動態運行時特性設計類似人工智能系統,提供基本原型,然后根據用戶自己的思維方式構建任意對象樹。技術科研是很不錯的方向,企業應用可能還有待商討。
以往我們很難在運行時為對象動態的添加屬性、行為、事件,通過動態語言運行時我們可以很自如的添加想要的東西。
下面我們來看一個簡單的例子,在運行時動態的構建一個對象類型,在以前我們只有用動態編譯、CodeDom技術來實現,這里將變的很簡單。
View Code
1 static void Main(string[] args) 2 { 3 dynamic objModel = new ExpandoObject();//初始化可以動態添加屬性、方法、事件的ExpandoObject對象 4 objModel.Name = "王清培";//設置屬性值 5 objModel.Age = 24; 6 objModel.WriteEvent = null;//存放事件的委托字段定義 7 objModel.WriteEvent += new Action<string>(WriteName);//設置事件的方法 8 objModel.WriteEvent(objModel.Name + objModel.Age); 9 Console.ReadLine(); 10 } 11 public static void WriteName(string info) 12 { 13 Console.WriteLine(info); 14 }
一個很簡單的例子告訴我們可以在C#中去編寫如JS中的動態對象功能,不過目前還不是很成熟,動態對象的成員沒有智能提示,應該是還沒有被大面積使用起來,以后肯定也是一大美餐;
總結:LINQ框架的基本使用原理就全部結束了,后面我們就來學習如何能讓LINQ查詢我們自定義的數據源。很多朋友都喜歡自己寫ORM框架,那么你肯定少不了對LINQ的支持吧?后面我們就來詳細的講解如何擴展IQueryable<T>、IQueryableProvider<T>兩個重量級接口,只有他們兩個才能讓我們和LINQ對話,這兩個接口還是很神秘的。
作者:王清培
出處:http://www.cnblogs.com/wangiqngpei557/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
