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()不會更新數據庫,也不會添加種子數據了。