The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

執行 Entity Framework 的「從資料庫更新模型」為何不更新

最近有同事遇到一個技術問題,他們的專案是採用 Entity Framework 技術,不過由於客戶端需求變更,導致資料庫欄位屬性被變更了,原本為 NOT NULL 的欄位從資料庫端已經改成了可允許 NULL,不過他們在 Visual Studio 2010 的 ADO.NET 實體資料模型設計工具 (ADO.NET Entity Data Model) 編輯 EDMX 檔案時發現執行 從資料庫更新模型 (Update Model from Database) 並不會自動反應資料庫的欄位變更到這裡,但是新增、刪除資料庫欄位時卻會更新,他們覺得很奇怪,以下就是我的回答。

首先我們先看看 ADO.NET 實體資料模型設計工具 的編輯畫面,當你資料庫有任何結構的改變,都可以在空白處按下滑鼠右鍵並點選「從資料庫更新模型」進行更新:

如果你有新增的表格、檢視或預存程序,可以在這時勾選,按下 [完成] 按鈕後就會自動加入 EDMX 檔,不過若是僅刪除物件 (表格、檢視、預儲程序) 或單純的單一表格欄位有所變動,什麼都不用勾選,直接按下 [完成] 按鈕即可完成重新整理!

 

我這邊整理了以下表格更新的情況,並分別解釋 ADO.NET 實體資料模型設計工具 會幫你做什麼事:

  • 新增表格
    • 會自動新增相關定義到 SSDL、CSDL 與 MSL 定義中
  • 刪除表格
    • 會自動刪除 SSDL 與 MSL 中所以與該表格相關的定義,CSDL 不會更新
  • 更新表格 ( 刪除欄位 )
    • 會自動自動刪除 SSDL 與 MSL 中所有該欄位的定義,CSDL 不會更新
  • 更新表格 ( 新增欄位 )
    • 會自動新增相關定義到 SSDL、CSDL 與 MSL 區段中
  • 更新表格 ( 欄位更名 )
    • 相當於先刪除欄位新增欄位
    • 舊欄位:會自動自動刪除 SSDL 與 MSL 中所有該欄位的定義,CSDL 不會更新
    • 新欄位:會自動新增相關定義到 SSDL、CSDL 與 MSL 區段中
  • 更新表格 ( 欄位變更屬性,例如: NOT NULL –> NULL )
    • 會自動自動更新 SSDL 中所有該欄位的定義,CSDL 會更新 ( 註: 這種情況下 MSL 本來就不需要更新 )

以上規則其實不用死背,只要對 ORM 的觀念有些瞭解,應該就會知道為什麼 Entity Framework 要這樣設計了。簡單的說,採用 ORM (Object-relational mapping) 技術其實就是希望開發人員從物件的角度來看這些實體(Entity),即便後端儲存的結構變了,如果有可能的話,也應該盡可能的維持 實體模型 (Entity Model) 的結構不改變,才能真正幫助開發人員在不瞭解實體資料結構的情況下進行開發,也因此你會發現上述的表格更新情況都只有在「新增資料庫物件」的情況下才能確保 EDMX 的定義能夠完整更新,當我們在變更刪除物件時 CSDL 都不會連帶更新!這觀念對 Entity Framework 的新手來說比較無法自己體會,但有人點你一下之後應該就會通了。

我剛開始使用 Entity Framework 時經常會有個疑問,就是不太能理解如下圖「驗證」功能的用途,我從來沒有驗證失敗過,其實是因為我在開資料表的時候通常不太會再去變動資料庫的欄位結構,所以不太有機會用到這功能,頂多客戶要加欄位時幫忙加上去,所以想當然爾的不會遇到驗證失敗的問題。

不過當你異動了特定表格欄位,例如將 NULL 改成 NOT NULL 或將欄位更名的情況之類的,進而導致 SSDL 更新了但 CSDL 卻沒有連帶更新,這時候就需要透過「驗證」功能幫你找出那些不合理的欄位。

例如你有個欄位叫做 Addr 儲存的是地址,當初資料庫開的限制條件時允許 NULL,而且你也已經建立好 EDMX 定義檔,這時你透過「從資料庫更新模型」進行更新時,就會發現 CSDL 一動也不動,但是 SSDL 的內容卻已經正確更新,這時你就可以透過執行「驗證」功能立即幫你找出以下錯誤:

錯誤 3031: 對應片段中從第 4497 行開始有問題:資料表 CareerNation 中不可為 Null 的資料行 CareerNation.RedirectURL 對應到可為 Null 的實體屬性。

當然你可能也會想說,我整份 EDMX 上所有的 實體模型 (Entity Model) 全部刪除後重新建立一次行不行?這當然可以!但是比較進階的 Entity Framework 應用來說,我們通常會修改掉一些 EDMX 中的導覽屬性 (Navigation Property),當名字手動改過你就不太可能再用全部砍掉重練的方式來更新 EDMX 定義,這時瞭解這些 Entity Framework 的細微觀念就更加重要。

當然,最重要也最實用的技巧就是到底實體模型的更新程序怎樣才是比較正確的呢?我針對那些只有 SSDL 更新而 CSDL 不會更新的情況一一做解說:

刪除表格

這最簡單,直接從 ADO.NET 實體資料模型設計工具 刪除這個 實體 (Entity) 即可:

 

更新表格 ( 刪除欄位 )

一樣直接從 ADO.NET 實體資料模型設計工具 刪除這個 屬性 (Property) 即可:

 

更新表格 ( 欄位更名 )

一樣直接從 ADO.NET 實體資料模型設計工具 刪除這個舊欄位對應的 屬性 (Property) 即可!

 

更新表格 ( 欄位變更屬性 )

我想這是最麻煩的地方了,因為很有可能你一次修改的欄位非常多,如果團隊之間沒有做好資料庫的版本控管,以致於根本不記得有哪些欄位型態被更新的話,那就麻煩大了,只能將整份 EDMX 砍掉重練一遍才行。

但如果你有辦法透過一些像是 Visual Studio 2010 資料庫專案的方式進行版本控管,就可以輕易的找出到底有哪些欄位需要被更新,這時你只要一一的針對那些欄位做出更新即可,雖然過程是繁瑣了點,但這件事畢竟不是經常在做也不應該經常做的事。

我個人也想到另一個也許還算方便的更新方法,那就是先透過 ADO.NET 實體資料模型設計工具 將那些更新過的實體屬性先刪除 ( 注意: 在 ADO.NET 實體資料模型設計工具 之中只能更新 CSDL 中的相關屬性定義 )。然後再透過 XML 編輯器開啟  EDMX 檔,手動將這幾個欄位一一刪除,當 EDMX 中的欄位刪除乾淨了之後,再重新執行一次「從資料庫更新模型」就可以完整重新新增 SSDL 與 CSDL 中的欄位到這裡來,你也就不用一一的透過屬性視窗來調整了,況且手動調整還有可能會調錯,透過這種方式也許還穩當些。

相關連結