在上一篇我花了一些時間談到,當我們載入資料庫模型是屬於 1 對多 (One-To-Many)時,預設值 Entity Framework 6 是利用一個稱為 Lazy Loading 機制,也就是當需要一個資料的其他相關(關聯)資料時,EF 才會產生SQL語法去跟後端資料庫要求相關資料,而資料應用程式效能的殺手之一,就是太繁複去跟後端資料庫來回存取,為了克服 EF 預設行為的效能問題,我想來介紹一個 EF 另外一個機制術語,稱為 Eager Loading ....


使用 Eager Loading 機制,主要可以在一開始讀取資料時,例如讀取 Customer Table 時,如果我們知道後續一定會讀取相關聯的資料表(類別),如案例的 CustomerCate Table、CustomerEvent Table,那麼我們就可以在第一次讀取 Customer Table 時就一次讀取所有相關的資料表,這樣後續應用程式需要相關資料時,只要在記憶體立即讀取,而不是需要用到時,才延遲去跟資料庫索取相關資料,解釋那麼多,到底程式語法該如何實作呢?我們會用到一個方法(Method)為 Include ....
PS.這次實作我完全根據 上一篇[Entity Framework] 效能深入探討(一) 繼續介紹,前端我暫時利用 Windows Form 來簡單的 Demo
,然後我會把焦點放在 Include Loading(1) 與 Include Loading(2) 的 程式語法
在 Include Loading(1) Button Click事件,我撰寫了一些程式碼:
- this.richTextBox1.Clear();
- this.richTextBox1.AppendText("使用 Eager Loading 機制 Include Method ! \r\n");
- this.richTextBox1.AppendText("------------------------------------------------------------------------- \r\n");
- using (CRMEF ef = new CRMEF())
- {
- var customers = ef.Customer.Include("CustomerCate").Include("CustomerEvent");
- foreach (var cust in customers)
- {
- this.richTextBox1.AppendText("客戶編號 : " + cust.No + " 客戶姓名 :" + cust.ENmae + "\t 類型 :" + cust.CustomerCate.Name + "\r\n");
- this.richTextBox1.AppendText("--------------------------------事件記錄------------------------------------ \r\n");
- foreach (var evt in cust.CustomerEvent)
- {
- this.richTextBox1.AppendText(evt.Desc + "\r\n");
- }
- this.richTextBox1.AppendText("\r\r\n");
- }
- }
注意上述程式碼的第 7 行我使用了2次 Include 方法,並在該方法傳遞是關聯類型的字串,透過這樣的方式,但程式第一次需要讀取 Customer 資料表時,
Entity Framewok(EF)就會自動產生的 SQL語法是把 Customer、CustomerCate、CustomerEvent 3個 Table,一次完全跟後端資料庫的索取,並記錄到記憶體(DataContext Cache),所以整個 SQL 傳輸只有 1 次,而不是上一篇介紹的 7 次哦(效能差很大,這也是一般書上很少寫的)。
底下我列出我追蹤的 SQL 語法如下
- SELECT
- [Project1] .[C1] AS [C1],
- [Project1] .[No] AS [No],
- [Project1] .[CName] AS [CName],
- [Project1] .[ENmae] AS [ENmae],
- [Project1] .[CateCode] AS [CateCode],
- [Project1] .[Code] AS [Code],
- [Project1] .[Name] AS [Name],
- [Project1] .[C2] AS [C2],
- [Project1] .[EventID] AS [EventID],
- [Project1] .[CustNo] AS [CustNo],
- [Project1] .[Desc] AS [Desc],
- [Project1] .[CreateDate] AS [CreateDate]
- FROM ( SELECT
- [Extent1] .[No] AS [No],
- [Extent1] .[CName] AS [CName],
- [Extent1] .[ENmae] AS [ENmae],
- [Extent1] .[CateCode] AS [CateCode],
- [Extent2] .[Code] AS [Code],
- [Extent2] .[Name] AS [Name],
- 1 AS [C1],
- [Extent3] .[EventID] AS [EventID],
- [Extent3] .[CustNo] AS [CustNo],
- [Extent3] .[Desc] AS [Desc],
- [Extent3] .[CreateDate] AS [CreateDate],
- CASE WHEN ([Extent3]. [EventID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
- FROM [dbo]. [Customer] AS [Extent1]
- LEFT OUTER JOIN [dbo] .[CustomerCate] AS [Extent2] ON [Extent1] .[CateCode] = [Extent2].[Code]
- LEFT OUTER JOIN [dbo] .[CustomerEvent] AS [Extent3] ON [Extent1] .[No] = [Extent3].[CustNo]
- ) AS [Project1]
- ORDER BY [Project1].[No] ASC, [Project1] .[Code] ASC , [Project1]. [C2] ASC
看吧看吧,是不是 EF很聰明的產生一個完全都包含的表格(Table)。
接下來,我們來看第二種 Include 語法的寫法,2種執行結果都相同。
- this.richTextBox1.Clear();
- this.richTextBox1.AppendText("使用 Eager Loading 機制 Include Method 2 ! \r\n");
- this.richTextBox1.AppendText("------------------------------------------------------------------------- \r\n");
- using (CRMEF ef = new CRMEF())
- {
- var customers = ef.Customer.Include(t => t.CustomerCate).Include(t => t.CustomerEvent);
- foreach (var cust in customers)
- {
- this.richTextBox1.AppendText("客戶編號 : " + cust.No + " 客戶姓名 :" + cust.ENmae + "\t 類型 :" + cust.CustomerCate.Name + "\r\n");
- this.richTextBox1.AppendText("--------------------------------事件記錄------------------------------------ \r\n");
- foreach (var evt in cust.CustomerEvent)
- {
- this.richTextBox1.AppendText(evt.Desc + "\r\n");
- }
- this.richTextBox1.AppendText("\r\r\n");
- }
- }
以上介紹完 Eager Loading Inclue 的技巧,希望各位操控 EntityFramework 6 可以更加熟析在最適當的狀況下,來調整預設存取行為機制。
感謝,很讚的分享!
回覆刪除