EntityFramework Core筆記(4)_延遲加載(執行)_IQueryable跟IEnumerable差異
延遲加載(執行):
屬於預設情況,當你查詢
一個集合就延遲,等到真正要使用存取該資料才去DB抓。
預設從DbContext存取的表資料回傳只要還沒去call .ToList() 或是 .ToArray() 等方法
都是回傳IQueryable<T> 都還不會真正跟DB有交握。
優點:
就在於可以降低阻塞,提升性能
需注意:
加載資料不能夠在DbContext被回收之後才運行
延遲加載(執行)-測試程式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | using EFCoreDbFirst_ConsoleApp.Models; using System; using System.Linq; namespace EFCoreDbFirst_ConsoleApp { class Program { static void Main(string[] args) { try { using (CustomerDbContext context = new CustomerDbContext()) { //Lazy Loading IQueryable<Commodity> commodities = context.Commodities.Where(item => item.Id == 20001); Console.WriteLine("開始存取"); foreach(var item in commodities) { Console.WriteLine(item.Title); } } } catch (Exception ex) { throw; } } } } |
所以可觀察到跑至foreach存取時才真正觸發底層查詢SQL語句跟DB交握
加載資料不能夠在DbContext被回收之後才運行-測試程式
所以跳脫出using區域後該context 就已經被釋放記憶體了
之後要再存取就會失效。
IQueryable跟IEnumerable差異
IQueryable 跟 IEnumerable 默認都是Lazy Loading
以下是測試程式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | using EFCoreDbFirst_ConsoleApp.Models; using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace EFCoreDbFirst_ConsoleApp { class Program { static void Main(string[] args) { try { //IQueryable 跟 IEnumerable 默認都是Lazy Loading IQueryable<Commodity> commodities = null; using (CustomerDbContext context = new CustomerDbContext()) { //Lazy Loading commodities = context.Commodities.Where(item => item.Id == 20001).AsQueryable(); Console.WriteLine("透過IQueryable存取"); foreach (var item in commodities) { Console.WriteLine(item.Title); } } IEnumerable<Commodity> commoditiesEnum = null; using (CustomerDbContext context = new CustomerDbContext()) { //Lazy Loading commoditiesEnum = context.Commodities.Where(item => item.Id == 20001).AsEnumerable(); Console.WriteLine("透過IEnumerable存取"); foreach (var item in commoditiesEnum) { Console.WriteLine(item.Title); } } } catch (Exception ex) { throw; } } } } |
都是到foreach開始遍歷存取後才去真正撈DB
若有多條件情況時
IQueryable(表達式查詢) : 表達式目錄樹拼裝
無論有多少where拼接都是合併在一起後才丟入DB做查詢
若有多個條件通常建議採用IQueryable
會多包含 IQueryProvider (表達式目錄樹轉換成SQL語句的驅動)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #region 組件 System.Linq.Expressions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a // C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Linq.Expressions.dll #endregion using System.Collections; using System.Linq.Expressions; namespace System.Linq { // // 摘要: // Provides functionality to evaluate queries against a specific data source wherein // the type of the data is not specified. public interface IQueryable : IEnumerable { // // 摘要: // Gets the type of the element(s) that are returned when the expression tree associated // with this instance of System.Linq.IQueryable is executed. // // 傳回: // A System.Type that represents the type of the element(s) that are returned when // the expression tree associated with this object is executed. Type ElementType { get; } // // 摘要: // Gets the expression tree that is associated with the instance of System.Linq.IQueryable. // // 傳回: // The System.Linq.Expressions.Expression that is associated with this instance // of System.Linq.IQueryable. Expression Expression { get; } // // 摘要: // Gets the query provider that is associated with this data source. // // 傳回: // The System.Linq.IQueryProvider that is associated with this data source. IQueryProvider Provider { get; } } } |
IEnumerable(迭代器):
則是在之前的where會直接去撈DB 再之後的則是去內存(緩存)做存取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | namespace System.Collections.Generic { // // 摘要: // Exposes the enumerator, which supports a simple iteration over a collection of // a specified type. // // 類型參數: // T: // The type of objects to enumerate. public interface IEnumerable<out T> : IEnumerable { // // 摘要: // Returns an enumerator that iterates through the collection. // // 傳回: // An enumerator that can be used to iterate through the collection. IEnumerator<T> GetEnumerator(); } } |
多條件情況-測試程式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | using EFCoreDbFirst_ConsoleApp.Models; using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace EFCoreDbFirst_ConsoleApp { class Program { static void Main(string[] args) { try { //IQueryable 跟 IEnumerable 默認都是Lazy Loading IQueryable<Commodity> commodities = null; using (CustomerDbContext context = new CustomerDbContext()) { //Lazy Loading commodities = context.Commodities .Where(item => item.Id == 20001) .AsQueryable() .Where(item => item.Title.Contains("test")) .Where(item => item.Title.Contains("1")); Console.WriteLine("透過IQueryable存取"); foreach (var item in commodities) { Console.WriteLine(item.Title); } } IEnumerable<Commodity> commoditiesEnum = null; using (CustomerDbContext context = new CustomerDbContext()) { //Lazy Loading commoditiesEnum = context.Commodities .Where(item => item.Id == 20001) .AsEnumerable() .Where(item => item.Title.Contains("test")) .Where(item => item.Title.Contains("1")); Console.WriteLine("透過IEnumerable存取"); foreach (var item in commoditiesEnum) { Console.WriteLine(item.Title); } } } catch (Exception ex) { throw; } } } } |
運行效果可觀察到
IQueryable(表達式查詢)
無論有多少where拼接都是合併在一起後才丟入DB存取的
IEnumerable(迭代器):
則是在AsEnumerable()之前的where會直接去撈DB
再之後的則是去內存(緩存)做存取,而非DB即時最新抓取。
留言
張貼留言