LINQ查詢方法一共提供了兩種擴展方法,在System.Linq命名空間下,有兩個靜態類:Enumerable類,它針對繼承了IEnumerable<T>接口的集合進行擴展;Queryable類,針對繼承了IQueryable<T>接口的集合進行擴展。我們會發現接口IQueryable<T>實際也是繼承了IEnumerable<T>接口的,既然這樣微軟為什么要設計出兩套擴展方法呢?
從LINQ查詢功能上我們知道實際上可以分為三類:LINQ to OBJECTS、LINQ to SQL和LINQ to XML。其實微設計這兩套接口主要是針對LINQ to OBJECTS和LINQ to SQL,兩者對於查詢的內部處理機制是完全不同的。針對LINQ to OBJECTS 時,使用Enumerable中的擴展方法對本地集合進行排序和查詢操作,查詢參數接受的是Func<>,Func<>叫做謂語表達式,相當於一個委托。針對LINQ to SQL時,則使用Queryable中的擴展方法,它接受的是Expression<>。
那么,到底什么時候使用IQueryable<T>,什么時候使用IEnumerable<T>?
首先我們來看一下LINQ to SQL的代碼:
using (var context = new NorthwindEntities()) { var orderTmp = context.Orders.Where(p=>p.CustomerID=="RATTC"); var orders = orderTmp.Where(p => p.OrderDate > new DateTime(1997, 1, 1)); foreach (var order in orders) { Console.WriteLine("OrderId:" + order.OrderID); } } |
通過vs的Intellisense我們可以看到Where的返回類型為IQueryable,參數是Expression類型的:
我們再看一下這一段代碼:
using (var context = new NorthwindEntities()) { var orderTmp = context.Orders.Where(p => p.CustomerID == "RATTC").AsEnumerable(); var orders = orderTmp.Where(p => p.OrderDate > new DateTime(1997, 1, 1)); foreach (var order in orders) { Console.WriteLine("OrderId:" + order.OrderID); } } |
這段代碼的不同在於我們將LINQ的查詢返回IEnumerable類型,我們看一下vs的Intellisense效果:
由於我們在LINQ查詢的時候加上了AsEnumerable(),因此我們在第二條語句能看到返回類型已經變為IEnumerable,參數也變成了Func<>類型。
至於這兩段代碼到底有什么區別,我們分別執行代碼,在sql profiler里看一下生成的sql語句:
第一段代碼效果:
雖然我們使用兩條語句進行了查詢,但最終只生成了一條SQL語句,將查詢參數合並了。
第二代碼效果:
這一次我們依然只看到一條SQL語句,但查詢條件也只有一個,但兩次查詢的結果是一致。
原因在於Func<>直接會被編譯器編譯成IL代碼,但是Expression<>只是存儲了一個表達式樹,在運行期作處理,LINQ to SQL最終會將表達式樹轉為相應的SQL語句,然后在數據庫中執行。
現在我們應該知道何時使用IEnumerable<T>,何時使用Iqueryable<T>。
以上內容部分參考《編寫高質量代碼改善C#程序的157個建議》。