在解決這個問題之後,我才開始覺得自己大概了解 Entity Framework Migration 的運作方式了....
無廢話版(TL;DR)
在多人共同開發的專案中使用 Entity Framework Migration 時,知道以下兩點可以讓我們少走一些冤枉路:
冗長版
我碰到的狀況有兩種:
無論是上述哪一種情形,若沒有使用正確的步驟來重建(re-scaffold)自己的 migration 版本,當 Entity Framework 在依序執行各 migration 版本來異動資料庫 schema 時,都很可能會出現類似底下的錯誤訊息:
The operation failed because an index or statistics with name IX_My_Index already exists on table.
看起來就像是我建立的 migration 被執行了兩次。
解決方法是先把資料庫退回(down grade)至上一個 migration 版本,然後用 Add-Migration -force 來重新建立我的 migration。
接著用一個實際案例來說明整個過程。
實際案例
Entity Framework 版本:v6.x
假設現在專案裡面已經有一個 migration 版本:201509010000001_Initial。Mike 和 John 都從目前的 Git 版本庫中的主分支建立了各自的工作分支,並且在接下來兩天裡面,也都在各自的工作分支裡面建立了 migration 版本:
從 migration 版本的時間戳記可以知道,John 建立 migration 的時間比 Mike 晚了一天。可是,
由於 John 比較快處理目前這項工作,所以先行 check-in 並且合併至主分支了。
再隔一天之後,Mike 也完成這項工作了,在 check-in 之前,依慣例先把主分支目前的版本拉回來合併至本機的工作分支。此時 Mike 發現 John 已經在主分支裡面加了一個新的 migration 版本。
於是,Mike 把整個資料庫刪除,然後執行應用程式,欲使 Entity Framework 按照底下的順序來重建整個資料庫:
結果執行時拋出異常,migration 執行過程發生錯誤(如前所述)。
解決方法
Mike 可以採取下列步驟來解決剛才的問題:
這樣就行了。
小結
其實只要了解 Code First Migration 的背後運作機制,就不至於像我那樣碰到一些奇怪狀況了。不明就裡地使用一項技術,終究會浪費許多時間在繞遠路和除錯上面,還不如先花點時間把它研究清楚。
故推薦這篇文章:Code First Migrations in Team Environments。文中有兩個影片,詳細說明了 Migration 背後的運作機制,是多人共同開發專案時必須了解的技術細節。
延伸閱讀
Happy coding!
無廢話版(TL;DR)
在多人共同開發的專案中使用 Entity Framework Migration 時,知道以下兩點可以讓我們少走一些冤枉路:
- 每次執行 Add-Migration 時,除了產生 C# 程式碼(裡面有 Up 和 Down 方法),還有一個 .resx 檔案。這個檔案很重要,裏面包含了目前 model 的快照(snapshot),而且會在變更資料庫 schema 時一併寫入 [__MigrationHistory] 資料表的 Model 欄位,以作為 model 版本差異比對之用。
- 使用 Add-Migration [目標名稱] 來產生一個遷移版本之後,如果後來又對 model 做了些變動,可以再執行相同的命令,以便重建(re-scaffold)現有的 .resx 檔案。如果要連舊眼的 C# 程式碼也重新產生,可加上 -force 參數,例如:
Add-Migration [目標名稱] -force
冗長版
我碰到的狀況有兩種:
- 在我的 work branch 已經建立某日期的 migration,可是在合併至 master branch 之前,其他人先一步把他的 branch 合併至 master。
- 與他人無關,我自己建立了 migration 之後,想要再改動資料模型。
無論是上述哪一種情形,若沒有使用正確的步驟來重建(re-scaffold)自己的 migration 版本,當 Entity Framework 在依序執行各 migration 版本來異動資料庫 schema 時,都很可能會出現類似底下的錯誤訊息:
The operation failed because an index or statistics with name IX_My_Index already exists on table.
看起來就像是我建立的 migration 被執行了兩次。
解決方法是先把資料庫退回(down grade)至上一個 migration 版本,然後用 Add-Migration -force 來重新建立我的 migration。
接著用一個實際案例來說明整個過程。
實際案例
Entity Framework 版本:v6.x
假設現在專案裡面已經有一個 migration 版本:201509010000001_Initial。Mike 和 John 都從目前的 Git 版本庫中的主分支建立了各自的工作分支,並且在接下來兩天裡面,也都在各自的工作分支裡面建立了 migration 版本:
- Mike:201509020000002_AddIndex
- John:201509030000003_AddTableZoo
從 migration 版本的時間戳記可以知道,John 建立 migration 的時間比 Mike 晚了一天。可是,
由於 John 比較快處理目前這項工作,所以先行 check-in 並且合併至主分支了。
再隔一天之後,Mike 也完成這項工作了,在 check-in 之前,依慣例先把主分支目前的版本拉回來合併至本機的工作分支。此時 Mike 發現 John 已經在主分支裡面加了一個新的 migration 版本。
於是,Mike 把整個資料庫刪除,然後執行應用程式,欲使 Entity Framework 按照底下的順序來重建整個資料庫:
- 201509010000001_Initial
- 201509020000002_AddIndex
- 201509030000003_AddTableZoo
結果執行時拋出異常,migration 執行過程發生錯誤(如前所述)。
解決方法
Mike 可以採取下列步驟來解決剛才的問題:
- 修改自己本機上面已經建立的 migration 版本名稱,把檔案名稱中的時間戳記的部分改成比較後面的日期。例如把原本的 201509020000002_AddIndex.* 改成 201509040000004_AddIndex.*。檔名改完後,還要修改 201509040000004_AddIndex.Designer.cs 的內容,將裡面的 Id 屬性的回傳值也改成相同的版本號碼(不可漏掉此步驟!)。
改好之後,此專案所包含的 migrations 依檔名的時間順序排列如下:
201509010000001_Initial
201509030000003_AddTableZoo
201509040000004_AddIndex
- 把目前本機的資料庫版本降回(downgrade to)至 201509030000002_AddTableZoo。一種比較保險的作法是把整個資料庫刪除,然後執行以下命令:
Update-Database -TargetMigration:AddTableZoo
當然,如果你確知目前資料庫所屬的 migration 版本(可透過 __MigrationHistory 資料表的內容得知),也不見得要先刪除整個資料庫。 - 重建(re-scaffold)自己的 migration 檔案。命令如下:
Add-Migration AddIndex -force - 最後再將資料庫 schema 更新至目前的版本:
Update-Database -TargetMigration:AddIndex
這樣就行了。
小結
其實只要了解 Code First Migration 的背後運作機制,就不至於像我那樣碰到一些奇怪狀況了。不明就裡地使用一項技術,終究會浪費許多時間在繞遠路和除錯上面,還不如先花點時間把它研究清楚。
故推薦這篇文章:Code First Migrations in Team Environments。文中有兩個影片,詳細說明了 Migration 背後的運作機制,是多人共同開發專案時必須了解的技術細節。
延伸閱讀
Happy coding!
沒有留言: