EFCore 2.1出來有一段時間了,裡面的新功能還沒怎麼用,今天研究下如何使用EF Core 2.1新增種子資料。
這部分的官方文件地址是:https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding
我們在開發時總是需要新增一些種子資料的,所以這個功能還是比較有用的。
準備工作
我建立了一個ASP.NET Core專案,裡面有幾個Model,其中一個是省份Province,另一個是城市City:
裡面還涉及到其它的Model,不過本文用不到,就不貼了。
該專案使用的資料庫是MSSQL LocalDB。並已經做好了上述Models的遷移工作。
該資料庫裡面存在過一些資料,但是現在都被我刪除了。
新增第一個種子資料
直接在DBContext的OnModelCreating方法裡使用HasData()方法:
這裡我新增了一個省份的種子資料,並寫上了主鍵Id的值。
資料庫該表的主鍵Id是int自增的。Id為1的資料曾經存在過,但是被我刪除了。
然後看看會發生什麼
生成的遷移類
命令:Add-Migration Xxx
看一下生成的遷移類的內容:
生成的SQL指令碼
命令:Script-Migration
這是裡面關於插入資料的部分:
遷移到資料庫
命令:Update-Database -Verbose
結果是成功的。
看紅線那兩句話,EFCore在執行的過程中臨時更改了設定,可以插入主鍵的值,然後又禁用了插入主鍵。
資料庫裡面的資料
雖然曾經存在過Id為1的資料(然後被刪除了),但是Id為1的種子資料仍然可以插入進去。
種子資料的主鍵必須有值
我再新增一個沒有主鍵Id值的種子資料:
然後Add-Migration,看看會發生什麼:
報錯了,所以主鍵值是必填的。
當我填寫了主鍵值之後,一切都是好用的了:
更改現有的種子資料
我在HasData方法裡更改了現有的種子資料,但是主鍵的值並沒有改:
執行Update-Database時的SQL語句:
可以看到是根據主鍵對資料庫裡面的資料進行Update動作。
其結果也和我想的一樣,就是更新了現有的資料:
如果我把HasData裡種子資料的主鍵值修改了
我把四川的主鍵從2改為3。
看下生成的遷移檔案:
先刪除了之前新增的Id為2的種子資料,然後把插入了一筆Id為3的資料。
看下SQL:
也是先Delete,再Insert。
資料庫裡:
種子資料為什麼要指定主鍵的值?
因為在團隊開發時,這樣可以確保不同的開發人員、電腦、伺服器上,在同一個遷移版本具有相同的種子資料。
新增關聯種子資料
Province和City是一對多的關係,也就是說一個Province可以有多個City,而且它們之間有導航屬性。
下面看看一次性新增Province和City是否可以行,我直接在HasData方法裡這樣寫:
然後Add-Migration
這樣做不行。我必須單獨新增City的種子資料,並且設定好外來鍵。
所以正確的做法是:
這次Add-Migration沒有報錯,遷移也成功了,看一下最後的資料:
OK
如果無法在Model裡設定主鍵/外來鍵
有時,我們在主從關係的Model裡不明確定義外來鍵;有時候我們Model的主鍵是private set的;
這時我們就無法在HasData裡設定主鍵/外來鍵的值了,那麼如何來新增種子資料呢?
答案就是使用匿名類。
我把City Model裡的外來鍵去掉(導航屬性仍然保留,和Province的主從關係依然存在):
然後就可以這樣新增種子資料:
遷移後的資料:
結果仍然如預期一樣。
如果主鍵是Guid型別呢?
看下資料:
貌似沒問題。
如果我不修改這個種子資料,再執行一次遷移呢?
看一下這時的遷移檔案:
刪除原來的資料,再插入一個新的資料。。
資料庫裡也是這樣的:
所以最好的辦法是把Guid的值放在一個變數裡:
然後再操作一遍:
這樣就不會出現“把原有資料刪掉,再重新插入”這種操作了。
其它
使用context.Database.EnsureCreated()會建立一個新的資料庫,幷包含有種子資料。但是如果資料庫已經存在了,那麼EnsureCreated()不會更新資料庫,也不會新增種子資料了。