本篇是相對獨立的一篇,主要講解不丢失資料進行資料庫結構更新。
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檔案夾
可以通過修改Configuration.cs來對Migration做一些配置(如加入一些測試資料等)
Note
如果前面沒修改web.config的資料庫名, 執行enable-migrations指令後,Migrations将會找到已有的資料庫MVCDemo然後自動執行add-migration指令。
當資料庫建立或資料庫結構更新後,這個方法會被調用,利用這個方法可以插入或更新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);
"dbo.SysUserRole",
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);
"dbo.SysUser",
UserName = c.String(),
Email = c.String(),
Password = c.String(),
}
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.
好了,今天就到這裡。
歡迎大家多多評論,讓下一篇文章更好 :)