1. 程式人生 > >IQueryable和IEnumerable以及AsEnumerable()和ToList()的區別

IQueryable和IEnumerable以及AsEnumerable()和ToList()的區別

注意:本文背景為 Linq to sql 。文中ie指代IEnumerableiq指代IQueryable

IQueryable 和 IEnumerable 的區別

  • IQueryable
    延時執行;擴充套件方法接受的是Expression(必須要能轉成sql,否則報錯)

  • IEnumerable
    延時執行;擴充套件方法接受的是Func(C#語法)

AsEnumerable() 和 ToList() 的區別

  • ToList()
    立即執行,載入資料到記憶體中。

  • AsEnumerable()
    延遲執行,真正使用時才載入資料。
    對IQueryable物件使用AsEnumerable()後,仍然是延遲執行,不過此時物件本質已經變了。
    前面已經說了IEnumerable的擴充套件方法接受的是Func(C#語法)

    ,當ie物件(iq轉變)真正使用時,會有2個步驟:
    1.它會把iq物件(轉變之前的)的擴充套件方法翻譯成sql語句,查詢出資料載入到記憶體中,變為ie物件;
    2.此時再把ie物件(轉變之後的)的擴充套件方法,使用C#求解,得到最終結果。
    例如:iq物件的Skip、Take方法,會被翻譯成sql,在資料庫裡執行取出最終結果。
    而ie物件的Skip、Take方法,則會取出全部資料到記憶體中,在記憶體中執行Skip、Take,會耗費大量資源。

誤區

  • 誤區1:對 iq物件 和 ie物件 使用foreach時,對於迴圈的每項都要查詢資料庫。

    錯誤!
    foreach針對的是資料集整體物件

    (列舉器?)。當使用foreach時,不管是iq物件還是ie物件,它們都是查詢資料庫一次,然後開始迴圈,直至迴圈結束。不過,當後續再次使用iq物件或ie物件的具體資料時,它們仍然會再次查詢資料庫。

結論

假設我們把最終資料之前的資料稱為中間資料,那麼:

  1. 中間資料只是作為條件篩選,需要的只是層層篩選之後的最終資料時,應該繼續使用IQueryable,防止載入不必要的資料到記憶體中。
  2. 存在中間資料,且中間資料被重複使用時,應該使用IQueryable.ToList()立即載入到記憶體裡使用(都被重複使用了,應該叫做最終資料了吧..);
  3. 如果中間結果無用,且想對IQueryable物件使用Func(C#語法)的擴充套件方法
    ,應該使用IQueryable.AsEnumerable()轉成IEnumerable物件,進行後續操作。

參考

  1. LINQ查詢中的IEnumerable<T>和IQueryable<T>
  2. LINQ使用細節之.AsEnumerable()和.ToList()的區別
  3. 建議29:區別LINQ查詢中的IEnumerable<T>和IQueryable<T> - 陸敏技《編寫高質量程式碼改善C#程式的157個建議》