本篇是相對獨立的一篇,主要講解不丟失數據進行數據庫結構升級。
前面我們講解EF功能時(見第三篇文章)已經介紹過一種更新數據庫的方式:
EF比較model和database,如果兩邊不一致,程序將會drop and re-create數據庫。
本篇文章我們會使用 code first migrations的方式。
這個功能可以使你改變data model,在不drop and re-create數據庫的情況下更新數據庫的結構,將這些改變部署到生產環境中。
下面就着重介紹如何使用此功能。
文章提綱
-
前置條件
-
啟用遷移功能
-
執行遷移
-
總結
前置條件
先回顧下之前EF修改模型的方式。
我們事先配置好EF,每次數據模型改變的時候都會drop and re-create數據庫。
例如你增加、刪除、改變實體類,或改變DbContext類后,運行程序時將會自動刪除已有的數據庫,創建一個新數據庫來匹配修改后的模型,同樣也會根據Seed方法中內容新建test data.
這種保持database和data model同步的方法在開發階段很方便。
如果已經部署到生產環境中就不行了, 例如表中擴充一些字段啥的, 原來的數據就不能丟失。
我們禁用原來更新數據庫的方式,將web.config中contexts配置節注釋掉。
另外我們不用原來數據,改下數據庫名,這樣可以生成一個新的數據庫,方便做實驗。
啟用遷移
下面就啟用Code First Migrations來解決數據庫更新的問題。
-
打開Package Manager Console
-
連續輸入如下指令:
enable-migrations 和 add-migration InitialCreate
enable-migrations指令:
a.在項目根目錄下創建了一個Migrations文件夾
b.在Migrations文件夾下新建一個Configuration.cs文件。
可以通過修改Configuration.cs來對Migration做一些配置(如加入一些測試數據等)
Note
如果前面沒修改web.config的數據庫名, 執行enable-migrations指令后,Migrations將會找到已有的數據庫MVCDemo然后自動執行add-migration指令。
類似我們第三篇文章中講到 DALàAccountInitilizer.cs類,Configuration類也包含一個Seed方法。
當數據庫新建或數據庫結構更新后,這個方法會被調用,利用這個方法可以插入或更新test data.
-
配置Seed方法
使用drop and re-create的方式時,因為每次model改變時數據庫都會被刪除,所有數據都會丟失,所以需要使用DALàAccountInitilizer.cs的Seed方法來插入測試數據。
使用Code First Migrations方式,當數據庫改變時測試數據會保留,所以包含test data的Seed方法一般來說是不需要的。
如果我們要部署數據庫到生產環境,事實上這種情況下我們也不想Seed方法來插入測試數據到生產環境中。
生產環境中用到Seed方法的例子: 比如在我們部署時獲得了實際的初始化數據,如實際存在的組織部門這些初始化的信息。
我們為了做實驗方便,還是插入一些測試數據。
先插入SysUser和SysRole
類似於前面文章提到的,Seed方法接收一個database context參數,利用這個參數添加新的實體到數據庫中。
對每個實體類型:
-
新建一個實體集合
-
添加到相應的DbSet屬性
-
保存更改到數據庫中
如果大家還記得之前的文章可以發現有一點不同,以前都是用Add方法,這次用了AddOrUpdate方法來插入數據。
sysUsers.ForEach(s => context.SysUsers.AddOrUpdate(p => p.UserName, s));
每次執行update-databse指令時都會執行Seed方法,一般來說,每次遷移后,你不僅僅是插入數據,例如你想要插入的數據在你創建數據庫的第一次遷移后已經在數據庫中了,這種情況下更新原有數據就可以了。
AddOrUpdate正好可以解決這個問題:
如果數據不存在,插入數據;如果數據存在,更新這筆數據。
context.SysUsers.AddOrUpdate(p => p.UserName, s)
第一個參數p.UserName就是檢查數據是否存在的主鍵。
我們的測試數據中,假設UserName都不能重復。
作為比較,我們添加SysUserRole實體類型的時候沒有使用AddOrUpdate,直接人為判斷是否存在,不存在再插入。
編譯下項目,下面開始更新數據庫。
執行遷移
前面執行 add-migration時,同樣在Migrations文件夾里面,產生一個<timestamp>_InitialCreate.cs的文件。
里面兩個方法,Up和Down:
Up方法創建數據庫表,Down方法刪除表。
public override void Up()
{
CreateTable(
"dbo.SysRole",
c => new
{
ID = c.Int(nullable: false, identity: true),
RoleName = c.String(),
RoleDesc = c.String(),
})
.PrimaryKey(t => t.ID);
CreateTable(
"dbo.SysUserRole",
c => new
{
ID = c.Int(nullable: false, identity: true),
SysUserID = c.Int(nullable: false),
SysRoleID = c.Int(nullable: false),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.SysRole", t => t.SysRoleID, cascadeDelete: true)
.ForeignKey("dbo.SysUser", t => t.SysUserID, cascadeDelete: true)
.Index(t => t.SysUserID)
.Index(t => t.SysRoleID);
CreateTable(
"dbo.SysUser",
c => new
{
ID = c.Int(nullable: false, identity: true),
UserName = c.String(),
Email = c.String(),
Password = c.String(),
})
.PrimaryKey(t => t.ID);
}
public override void Down()
{
DropForeignKey("dbo.SysUserRole", "SysUserID", "dbo.SysUser");
DropForeignKey("dbo.SysUserRole", "SysRoleID", "dbo.SysRole");
DropIndex("dbo.SysUserRole", new[] { "SysRoleID" });
DropIndex("dbo.SysUserRole", new[] { "SysUserID" });
DropTable("dbo.SysUser");
DropTable("dbo.SysUserRole");
DropTable("dbo.SysRole");
}
下面我們就執行正式遷移。打開Package Manager Console
輸入 update-database
update-database指令調用了Up方法來新建database的表(和data model entity set對應), 然后調用Seed方法來填充測試數據。
這個時候測試下程序,打開數據庫看下,完全符合我們的預期。
再進一步,我們添加一個表Test
先添加一個Model
修改AccountContext.cs, 增加一個data model entity set
執行add-migration AddTestTable和update-database, 完成數據庫表的添加。
去數據庫中檢查,發現已經多了Test這張表了。
最后再檢查下新產生的配置文件。
大家現在應該能充分理解到add-migration時產生的文件的作用了吧。
總結
本次我們主要講解了數據庫遷移/升級的問題。
主要分為 啟用遷移(enable-migrations) 和 執行遷移(add-migration, update-database) 兩大步驟。
啟用遷移:產生遷移相關文件夾Migrations和文件夾中相關的配置文件。
執行遷移:產生相關的遷移更改文件並執行更改。
請充分理解本篇文章所講的例子, 后面文章會多次用到add-migration.
好了,今天就到這里。
歡迎大家多多評論,讓下一篇文章更好 :)