1. 程式人生 > >.NET深入解析LINQ框架(三:LINQ優雅的前奏)

.NET深入解析LINQ框架(三:LINQ優雅的前奏)

對話 spa log 有用 強類型 provider 瓶頸 模式 是什麽

閱讀目錄:

  • 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)重點”的地方,我們很清楚的看見表達式樹被動態編譯後然後緊接著又被執行,這裏就能看出為什麽IEnumerable<T>對象需要能夠被轉換成IQueryable<T>對象。這樣就可以消除IEnumerable<T>、IQueryable<T>這兩個接口之間的動態查詢瓶頸。

為什麽需要動態LINQ查詢,上面說過問題出在我們沒辦法在運行時再去編寫Lambda表達式了,都知道Lambda表達式到最後就是被編譯成Expression表達式樹對象,所以我們可以在運行時自己動態的構建Expression對象,這樣就可以將動態構建出來的表達式樹對象直接傳入到需要的方法中。如果查詢的數據對象是IEnumerable<T>則會被動態編譯成可以執行的委托然後直接執行,如果查詢的是IQueryable<T>則順其自然的被提供程序解析執行。

下面我們來看一個簡單的動態查詢例子:

技術分享View Code

這是一組數據,為了簡單測試就不搞那麽麻煩的Linq to Sql數據源了。我們將要通過動態的構建表達式樹來做為查詢的邏輯,以往我們的Lambda在這個時候派不上用場了,在運行時我們無法再去構建委托類型。

現在的需求是從界面上接受一個Name值的輸入,LINQ的查詢只需要直接寫就行了。

技術分享View Code

但是我們需要動態的構建表達式樹來執行查詢,表達式樹的任何一個節點都有相對應的Expression派生類型,所以我們只要將相關類型組裝起來就行了。由於我建的示例程序的類型是控制臺程序,所以我們就用簡短的方式演示一下如何構建表達式樹。

技術分享View Code

圖例:

技術分享

該例子的重點是如何動態構建邏輯,根據不同的項目要求完全可以將類似的功能封裝起來供以後重復使用。如果覺得手動編寫表達式樹很麻煩的話,建議可以找一個輔助類能將Lambda表達式的對象樹都能打印出來的工具,然後對著這棵樹在去寫就簡單多了。

關於動態LINQ的第三方的API不是很多,比較常用的就是Dynamic.cs的使用,具體我沒有用過,看過相關文檔應該還是比較方便的。它的內部原理其實還是動態的構建表達式樹,只不過這部分工作被人家做了,而我們使用起來卻簡單的很多。

2】.DLR動態語言運行時(基於CLR之上的動態語言運行時)

從C#1一路走來,它變的越來越強大,.NET平臺變得無所不能。很多人還一直咬著.NET不能跨平臺,不能支持動態對象,不支持非托管等等理由來排斥它,然而他們所不知的是.NET已經悄無聲息的做出來一大舉動,那就是在靜態語言運行時上嵌入動態語言運行時環境。我想不是微軟不能支持所謂的缺點,而是它確實有它的本意。

動態語言運行時是在.NET4.0中引入的建立在CLR之上的運行時環境,目的是為了在靜態語言中能夠借鑒動態語言運行時的優點,比如強大的類型隨意變換,這點在設計應用開發框架時尤其重要,任何一個好的特性都需要大面積的使用模式才能變的更完美。

說到動態運行時就不得不提JS中讓人興奮的var定義的對象特性了,如果沒有留意在設計框架時而存在的煩惱其實很難發現動態運行和靜態語言之間的好與不好。很明顯的例子就是當我們定義一個數據類型的對象時,無法再在後期運行時對它進行其他類型的使用,看一個簡單的例子:

技術分享View Code

在運行時我們可以隨意的設計對象的類型,我大膽的假設完全可以用動態運行時特性設計類似人工智能系統,提供基本原型,然後根據用戶自己的思維方式構建任意對象樹。技術科研是很不錯的方向,企業應用可能還有待商討。

以往我們很難在運行時為對象動態的添加屬性、行為、事件,通過動態語言運行時我們可以很自如的添加想要的東西。

下面我們來看一個簡單的例子,在運行時動態的構建一個對象類型,在以前我們只有用動態編譯、CodeDom技術來實現,這裏將變的很簡單。

技術分享View Code

一個很簡單的例子告訴我們可以在C#中去編寫如JS中的動態對象功能,不過目前還不是很成熟,動態對象的成員沒有智能提示,應該是還沒有被大面積使用起來,以後肯定也是一大美餐;

總結:LINQ框架的基本使用原理就全部結束了,後面我們就來學習如何能讓LINQ查詢我們自定義的數據源。很多朋友都喜歡自己寫ORM框架,那麽你肯定少不了對LINQ的支持吧?後面我們就來詳細的講解如何擴展IQueryable<T>、IQueryableProvider<T>兩個重量級接口,只有他們兩個才能讓我們和LINQ對話,這兩個接口還是很神秘的。

.NET深入解析LINQ框架(三:LINQ優雅的前奏)