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即時最新抓取。

















留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header