MVC5+EF6 入門完整教程八

MiroYuan發表於2014-12-15

本篇是相對獨立的一篇,主要講解不丟失資料進行資料庫結構升級。

前面我們講解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來解決資料庫更新的問題。

  1. 開啟Package Manager Console

  1. 連續輸入如下指令:

    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.

  2. 配置Seed方法

使用drop and re-create的方式時,因為每次model改變時資料庫都會被刪除,所有資料都會丟失,所以需要使用DALàAccountInitilizer.cs的Seed方法來插入測試資料。

使用Code First Migrations方式,當資料庫改變時測試資料會保留,所以包含test data的Seed方法一般來說是不需要的。

如果我們要部署資料庫到生產環境,事實上這種情況下我們也不想Seed方法來插入測試資料到生產環境中。

生產環境中用到Seed方法的例子: 比如在我們部署時獲得了實際的初始化資料,如實際存在的組織部門這些初始化的資訊。

我們為了做實驗方便,還是插入一些測試資料。

先插入SysUser和SysRole

類似於前面文章提到的,Seed方法接收一個database context引數,利用這個引數新增新的實體到資料庫中。

對每個實體型別:

  1. 新建一個實體集合
  2. 新增到相應的DbSet屬性
  3. 儲存更改到資料庫中

如果大家還記得之前的文章可以發現有一點不同,以前都是用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.

好了,今天就到這裡。

歡迎大家多多評論,讓下一篇文章更好 :)

相關文章