目錄html
標籤: blogsql
這3個方法的功能徹底不一樣, 應按照具體業務場景使用.數據庫
如下是一段最多見的代碼,mvc
var products = db.Product.where(p => p.Type == "food").select(p => new { p.Id, p.Name, p.CreateTime});
products 的類型爲 IQueryable<T>
, 所以不用加 .AsQueryable()
. 語句執行後不會馬上查詢數據庫, 而是在迭代使用 products 時纔會查數據庫, 具備 lazy load 的特性, 按需查數據庫可提升程序效率.框架
迭代時上面的代碼生成的 sql 語句相似:性能
select Id, Name, CreateTime from Product where Type = 'food'
對 products 再次使用 IQueryable 操做, 編譯器會把結果合併爲1條 sql 語句, 以下,code
var products = db.Product.where(p => p.Type == "food").select(p => new { p.Id, p.Name, p.CreateTime}); var orderedProducts = products.OrderBy(p => p.CreateTime);
迭代時生成的 sql 語句相似:htm
select Id, Name, CreateTime from Product where Type = 'food' order by CreateTime
若是對沒有繼承 IQueryable 接口的類型使用 AsQueryable(), 會轉換類型, 稍微消耗資源, 以下,blog
int[] array = new { 1, 2, 4, 5}; var enumArray = array.AsQueryable();
由於 Array 只繼承了 IEnumerable, 沒有繼承 IQueryable, 因此會發生類型轉換. 固然其中的泛型實現沒有拆箱, 對性能影響不大. 可是沒有必要, 由於 IQueryable 沒有聲明任何新方法.繼承
其餘有用的狀況我暫時還沒碰到, 請你們指教.
此外 IQueryable 有諸多限制, 只支持數據庫查詢語法, 沒法支持 Linq to object 的操做, 如如下2段代碼會在運行時出錯,
var products = db.Product .Where(p => p.Type == "food") .Select(p => new { p.Id, p.Name, p.CreateTime.Date}); // 若是改爲 .Select(p => new { p.Id, p.Name, DbFunction.TruncateDate(p.CreateTime)}) // 就能正常運行.
var products = db.Product .Where(p => p.Type == "food") .Select(p => MyMethod(p));
.Select() 的返回類型爲 IQueryable.
第1段代碼, 我認爲 IQueryable 還不夠智能, 沒法把 p.CreateTime.Date 轉換爲 sql 相關的 function, 而使用 DbFunctions 要求使用者瞭解同時熟悉 linq to entity 及 sql 的內置方法.
第2段代碼, 生成 的 sql 沒法返回 MyMethod 類型, 是能夠理解的.
可是, 對代碼加了 AsEnumerable() 後運行正常, 由於 IEnumerable 支持 Linq to object 的操做.
一樣是延遲查詢, 與 AsQueryable() 區別是, 迭代時遇到 AsEnumerable() 會先進行 sql 查詢, 因此對已查出來的結果固然能進行 Linq to object 操做.
可是, 千萬不要爲了方便而濫用 AsEnumerable(), 可能會嚴重消耗資源, 以下代碼,
var products = db.Product.AsEnumerable() .Select(p => new {p.Id, p.Name, p.CreateTime.Date});
上面的代碼在查詢時會把整個Product表的結果存放進內存, 而後進行 .Select 查詢!!!
正確的作法應該以下,
var products = db.Product .Select(p => new {p.Id, p.Name, p.CreateTime}) .AsEnumerable() .Select(p => MyMethod(p));
若是你寫了如下代碼,
var products = db.Product .Select(p => new {p.Id, p.Name, p.CreateTime}) .AsEnumerable() .Select(p => new {p.Id, p.Name, p.CreateTime.ToString()}); .AsQueryable() .Where(p=> p.Name == "xxx");
那麼最後的 .Where()永遠不會執行!!!
由於在使用 AsQueryable().Where() 要求 Linq to entity 進行數據庫查詢, 可是第一次 AsEnumerable() 時已經進行了數據庫查詢而且斷開鏈接, 而且查詢結果已經做爲實實在在的 object, 對 object 不可能再次使用 AsQueryable().Where() 疊加數據庫查詢!
調用 ToList() 會馬上查詢並保存結果, 而不會等到迭代時才查詢. 做用和 lazy load 是對立的.
在須要獲得完整結果後, 再處理的場景, 須要使用 ToList().
例如, 在返回mvc ActionResult 的時候, 要先使用 ToList(), 再做爲 model 傳給 view. 不知道 mvc 是出於什麼考慮不支持在生成 html 的時候lazy load, 請你們指教!
使用 EF 也蠻久了, 確實方便, 可是不熟悉其中原理的話難免要踩坑, 目前正在掙扎從坑裏面爬出來lol.
有錯的地方請你們指出, 歡迎拍磚.
參考 StackOverflow: What's the difference(s) between .ToList(), .AsEnumerable(), AsQueryable()?
看到評論指出 .AsEnumerable().AsQueryable().Where(...)
, Where
條件也會生效. 確實文中這一部分描述有誤.
實際狀況是, 在迭代時遇到AsEnumerable()
會先把數據裝入內存, 以後調用AsQueryable().Where()
, 但這時候底層的類型是IEmumerable
而不是IQueryable
, 因此框架會使用IEnumeralbe.Where()
, 所以可以過濾數據. 然而, 當繼續使用.Include()
, .Where(i => DbFunctions...)
等IQueryable
獨有的方法時, 框架會使用空實現, 沒有效果.