﻿# Entity Framework 學習心得分享 ( 以 LINQ to SQL 為基礎 )

剛花了些時間靜下心來讀了些 Entity Framework 相關資料，終於對 Entity Framework 有了些清楚的輪廓，由於我之前 LINQ to SQL 還算有點瞭解，對 ORM ( [Object-relational mapping](http://en.wikipedia.org/wiki/Object-relational_mapping) ) 也還算有點概念，所以在這樣的基礎之下，我相信要上手 Entity Framework 應該不是什麼難事。

我之前在從未看過任何一篇 Entity Framework 文章的情況下 ( 只看過一些入門教學影片 )，自己嘗試著試寫 Entity Framework 專案，說實在的還真的蠻挫折的，今天我就分享 4 個我所遭遇到的挫折與解決方法：

**1\. 使用 LINQ to Entity 預設不會自動載入關連實體(Entity)的資料**

如下圖示，左圖是我在 ASP.NET MVC 中的Controller Action，右圖是 MVC 的 View，這段 Code 是無法執行的，因為 Entity Framework 並未自動載入 item.Category 的值，所以 item.Category.Title 將會引發例外事件。

![return View(db.News);](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/e7693041371f20703db1-image_9.png "return View(db.News);")![<%= Html.Encode(item.Category.Title) %>](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a1dd76ed942cb0a2199d-image_8.png "<%= Html.Encode(item.Category.Title) %>")

而我最後研究出來的載入方法有兩種：

1.  在 Action 裡取得 News 資料時就載入 Category 資料（如下左圖）  
    
    ```
    return View(db.News.Include("Category"));
    ```
    
2.  在 View 中執行關連資料載入的語法（如下右圖）
    
    ```
    item.CategoryReference.Load();
    ```
    

[![return View(db.News.Include("Category"));](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a62d2bf05f9c4aaf5c0d-image_thumb_3.png "return View(db.News.Include(\"Category\"));")](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/5fbe59a5dc2aaf27cdb2-image_11.png) ![<% item.CategoryReference.Load(); %>](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/d05228cbd722df9a23a0-image_16.png "<% item.CategoryReference.Load(); %>")

我也有將 SQL Server Profiler 打開，也發現第 1 種比較有效率，因為只要一個 SQL 查詢就可以取得所有 Entity 所需的資料。而第 2 種方法則是在每一個 foreach loop 都會向資料庫查詢資料一遍，AP 與 DB 之間的 round trip 就會非常多，當資料量大時效能就會變差。

**2\. 更新關連欄位的資料也頻頻失敗**

以更新關連資料為例，我有兩個彼此相關的表格，分別為 Category ( 新聞類別 ) 與 News ( 新聞資料 )，News 表格中有個 CategoryID 欄位，但因為 Entity Framework 的關係，你只有 Category 這個屬性可以存取，所以你就無法直接更新 CategoryID 這個欄位的值，反而要先取出 Category 的資料，再指定給要更新的那筆 News 紀錄。我當時的寫法如下：

```
News n = db.News.First(p => p.ID.Equals(id));   
n.Category = db.Category.First(p => p.ID == new Guid(Request.Form["CategoryID"]));
```

很不幸的，這段 Code 執行時會引發以下錯誤：

```
Only parameterless constructors and initializers are supported in LINQ to Entities.
```

而實際上正確的寫法如下：

```
News n = db.News.First(p => p.ID.Equals(id));
Guid cid = new Guid(Request.Form["CategoryID"]);
n.Category = db.Category.First(p => p.ID.Equals(cid));
```

**3\. 當使用 uniqueidentifier 當主索引鍵時在建立資料時需明確指定**

這也不算是問題，只是當我資料庫中使用 uniqueidentifier 當主索引鍵時，在建立資料時就必須一定要指定明確的欄位植給他，否則就算在資料庫中有設定 newid() 當預設值，Entity Framework 在執行 SaveChanges() 時還是會以 00000000-0000-0000-0000-000000000000 進行新增動作。

不過若使用 int 當主索引鍵時，並設定 Identity Specification 為 Yes 的，在新增資料時就可以將設定 id 欄位的程式碼省略。

很可惜 Entity Framework 不像 LINQ to SQL 有 OnCreated 部分方法(Partial Method)，可以在 OnCreated 方法中指定 Entity 的 ID 值，否則應該有些重複的程式碼可以寫在這個事件裡。

**4\. 在 Entity Framework 中找不到 OnValidate 方法，不知如何驗證商業邏輯規則(Business Logic)**

以往在 LINQ to SQL 中，每一個 Entity 內都可以實做一些 partial method，例如 OnCreated, OnValidate 方法，其中 OnValidate 我就覺得很好用 ( [善用 LINQ to SQL 中的 partial class 機制進行資料格式驗證](/post/2008/06/16/Use-OnValidate-partial-method-on-LINQ-to-SQL-to-do-business-logic-validation) )，只是在 Entity Framework 不叫做 OnValidate 方法，而且定義的位置也不在 Entity 裡，而是改在 ObjectContext 裡先實做 OnContextCreated 方法(partial method)，然後在該事件裡再定義一個 **SavingChanges** 事件委派，然後在到委派的方法中進行商業邏輯驗證，詳細的說明可參考[這篇](http://msdn.microsoft.com/zh-tw/library/cc716714.aspx)文章：[HOW TO：在儲存變更時執行商務邏輯 (Entity Framework)](http://msdn.microsoft.com/zh-tw/library/cc716714.aspx)

\---

學習新技術時，常常就是會因為卡在這一些小地方，有時後就足以讓嚐鮮者卻步，或是對新技術做出不正確的評價。

**相關連結**

-   [使用物件 (Entity Framework 工作)](http://msdn.microsoft.com/zh-tw/library/bb738459.aspx)
-   [Entity Framework Query Samples](https://web.archive.org/web/20080703212217/http://code.msdn.microsoft.com/EFQuerySamples)
