Code First 大道番外-Migrations


技術探討 2017/05/25
其實從 Code First 大道逃跑並不是那麼簡單的一件事情,至少還有守衛在呢!那守衛可是 Code First 大人的左右手,如同大學教授以及其研究助理一般的存在。那麼小皮球是如何躲過守衛的防備呢?

小皮球:「為什麼要用躲的啊?要打開大門的鑰匙與密碼都在守衛手裡呢。」
作者:「所以我很好奇……你那時候交給守衛的紙條是什麼?竟然讓守衛幫你開門……」
小皮球:「嘿嘿~因為那時候守衛大人可是急欠了 Migrations 的研究範本嘛,所以我只是把ITE2 NAS上面Migrations範本的Share Link分享連結給他就好了啊!我可是幫了他大忙喔!」

由此可知,逃離 Code First 大道之必備工具:「Migrations」,有人翻譯為資料庫遷移,主要是讓人方便地更改資料庫的 Schema。

所以接下來,就來看看小皮球的逃跑工具「Migrations」吧!

先準備好資料表的Model、以及DbContext的類別。

小皮球一開始只準備了一個 Model 如下:



連接資料庫的DataAccess類別:



小皮球只在 config中加入了一個連線字串:



程式碼執行內容只是加入一筆資料:



於是就建立了最初的資料庫:



由於 Migrations 是要透過下 NuGet Command 的方式執行,所以需要開啟 NuGet 管理器主控台(可從「工具」>「NuGet套件管理員」>「套件管理器控制台」進入):



在下指令之前,記得先把「預設專案」設定為要執行 Migrations 的專案,以小皮球的例子來說,就是 TestMigrationEF。

基本上,第一個指令都會是「Enable-Migrations」,先啟用 Migrations 再說吧!一旦啟用之後,專案中會自動增加了 Migrations 的資料夾、Configuration.cs、以及第一個版本的 {XXXXXXXXXXXXXXX}_InitialCreate.cs 檔案。



先來看一下Configuration.cs,裡面的 Seed 方法,這是當程式把資料庫版本更新到最後一個版本後就會執行的,用來提供塞入預設資料。



再來說說第一個版本的 {XXXXXXXXXXXXXXX}_InitialCreate.cs 檔案吧!打開看就不難發現資料庫的 Schema 已經寫好在裡面了。這裡只會有兩個方法:Up、與Down,分別表示了資料庫升降級即資料庫版本異動時,所要執行的動作;而身為第一個版本的 InitialCreate 自然就是當資料庫建立時所要執行的動作。




通常不太需要在這裡做任何修改,但如果有打算調整的話,也可以考慮在此處加入 Index:



加了之後,可以嘗試把原先的資料庫刪掉、或者在連線字串中更換資料庫名稱,用以進行重建資料庫;小皮球選擇的方法就是後者,直接建一個新的資料庫以檢查是否有一個 Index存在(如果只是單純用 Code First 建立資料庫的話,Entity Framework預設是不會幫忙建立 Index 的)、並檢查是否有預設資料。

準備好後,就下指令:「Update-Database」以建立資料庫或版本更新。






接著來異動一下 Schema 吧!

小皮球很簡單地又加入了第二個資料表的 Model去做關聯原先的第一個資料表:






接著,小皮球就下指令:「Add-Migration InitialCreate」,於是就可以看到第二個版本的 {XXXXXXXXXXXXXXX}_InitialCreate.cs 檔案。



作者:「然後你就要執行 Update-Database 了嗎?」
小皮球:「當然,不然我要怎麼更新Schema。」
作者:「……我先等等看有沒有好戲發生好了。」
小皮球:「為什麼要這樣說?!你這樣給我一種不安的預感耶!」
作者:「我先泡茶好了,等等你哀哀叫完還可以潤喉一下。」
小皮球:「……什麼意思啊?!」

結果證明,小皮球執行「Update-Database」是失敗的,失敗原因倒是有顯示出來:
「System.Data.SqlClient.SqlException (0x80131904): ALTER TABLE 陳述式與 FOREIGN KEY 條件約束 "FK_dbo.Employees_dbo.Departments_DepId" 衝突。衝突發生在資料庫 "MigrationEF2",資料表 "dbo.Departments", column 'Id'。」
作者:「不意外吧?原先又沒有 Department 資料、甚至連資料表都沒有,你又限制 Employee 一定要對應到 Department,那目前在資料庫中的那筆資料怎麼辦?」
小皮球:「對吼!」

於是小皮球先把關聯拿掉,包含 Employee 中的 department、與 DepId,Department 中的 employee;並手動調整每次版本更新Up方法。
首先,第二個版還不可以加入關聯,所以第二個版本小皮球就讓 Migrations 只執行加入 Department 資料表:




並讓Seed方法中加入一筆資料:



這樣一來成功執行「Update-Database」了。

接著再把之前拿掉的關聯還原、再加入一個版本,讓第三個版本執行加入關聯以及欄位,記得讓 Foreign Key 有一個預設值(須確認資料庫中有一筆 Department 資料、且 Id 為預設值),否則可能會因為舊的資料沒有預設值導致失敗。




然後才下指令「Update-Database」。



值得一提的是,大家可以觀察一下 __MigrationHistory 這個資料表,它會記錄著每一次的版本更新。




作者:「所以說~ 大家要先等小皮球全部做完、好戲看完之後再來學習 Migrations 喔!」

小皮球:「……所以說,為什麼你不要再一開始的時候就先提醒我嘛!」

作者微微一笑:「有好戲看我為啥不看?」

小皮球:「……作者你這個壞人!」



《完》


補充相關參考:

Migrations in MVC

Code First Migrations

Migrations(資料庫遷移)

Entity Framework Nuget Console commands




上一篇下一篇
  • 115 台北市忠孝東路六段21號10樓之2
  • Tel:(02)2788-0855
  • Fax:(02)2788-0655
  • Copyright © iTe2 Technology Inc.保留一切權利