使用 Entity Framework 的 Code First 來做資料存取層與既存資料庫溝通時多少都會遇到資料庫 Schema 變更的時候,只是這個時候若是有用到 Code First 來新增表格的話就會遇到像上一篇一樣的問題;執行時期使用資料庫的話會引發錯誤訊息,而且會通知因為資料庫的 Schema 變更需要使用 Migrations 來解決問題。因此這篇文章就是要來說明一般來說使用 Code First 來開發遇到資料庫 Schema 要變更時的正規作法應該怎麼做。

我們就用上一篇所生出來的資料庫來實作看看吧~

DB_Schema

不過為了方便起見,我們就直接先把 Products 這個資料表先刪除,我們來看看用 Code First 的方式開發的話若要新增一個資料表的正規方式是什麼?(這邊正規也只是我在講而已,只是這麼做的話可以免除之後的麻煩) 首先我們要先打開套件管理員的命令列視窗「Tools => Library Package Manager => Package Manager Console」,然後要用 Migrations 的話就要先把功能打開,需要下這樣的指令:

1
PM >> Enable-Migrations

,成功的同時我們的專案會新增一個 Migrations 資料夾,並且會給你一個 Configrations.cs 類別,若沒有要使用自動 Migrations 的話就可以先不用動這個類別的內容。接下來因為需求異動的關係我們需要多一個 Products 資料表來儲存產品的資訊。 Products 的資料表如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/// 產品的實體。
[Table("Products")]
public class Product
{
    /// 產品的識別號。
    [Key]
    [ScaffoldColumn(false)]
    public int ID { get; set; }

    /// 產品的名稱。
    public string Name { get; set; }

    /// 產品的價格。
    public double Price { get; set; }

    /// 生產產品的工廠名稱。
    public string FactoryName { get; set; }
}

對於資料庫的 Schema 的異動我們可以在 Package Manager Console 中輸入 Add-Migrations xxxxxx xxxxxx就是新增的類別的名稱,通常會是這次異動的表格、欄位的名稱像是 AddProducts 之類的。這邊我是用 AddTableProducts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
using System;
using System.Data.Entity.Migrations;

public partial class AddTableProducts : DbMigration
{
    /// Operations to be performed during the upgrade process.
    public override void Up()
    {
    }

    /// Operations to be performed during the downgrade process.
    public override void Down()
    {
    }
}

使用 Migrations 時一定要實作這兩個方法,而且內容要互相對應。 什麼意思呢?就是說若今天是要新增一個 Table 的話,那我們就要在 Up() 中放置新增 Table 的程式,而在 Down() 中放置捨棄該 Table 的程式像是底下這樣:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public partial class AddTableProducts : DbMigration
{
    public override void Up()
    {
        // 新增一個資料表就呼叫CreateTable方法,需指名資料表明稱,以及資料表各欄位的資料型態
        CreateTable(
            "dbo.Products",
            c => new
                {
                    ID = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                    Price = c.Double(nullable: false),
                    FactoryName = c.String(),
                })
            .PrimaryKey(t => t.ID);
    }

    public override void Down()
    {
        // Down 這邊要放是 CreateTable的相反方法,這邊就是 DropTable方法
        DropTable("dbo.Products");
    }
}

寫完之後我們在 Package Manage Console 中下個 Update-Database 指令,順利執行完畢之後我們去資料庫中就可以看到新增後的資料表了~ 至於新增一筆欄位我們也可以如法炮製,若我們現在 Products 資料表又多了一筆欄位的需求,要把產品的庫存數量記錄下來,首先我們先在 Model 中修改新的 Products 資料表的 Schema (我們在最後面新增了一個屬性"Quantity"):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/// 產品的實體。
[Table("Products")]
public class Product
{
    /// 產品的識別號。
    [Key]
    [ScaffoldColumn(false)]
    public int ID { get; set; }

    /// 產品的名稱。
    public string Name { get; set; }

    /// 產品的價格。
    public double Price { get; set; }

    /// 生產產品的工廠名稱。
    public string FactoryName { get; set; }

    /// 產品於庫存的數量。
    public int Quantity { get; set; }
}

好哩~這樣 Code 就算改好了,接下來我們只要下個確認異動的指令讓資料庫異動就好了~ 我們在 Package Manage Console 中鍵入「Update-Database」等待執行完畢我們就可以去資料庫確認一下異動是否成功了(其實只要 Console 沒有吐出例外訊息應該都是沒有問題的)

MigrationColumn

噹~噹~ Great!資料庫果然真的有這麼一個欄位在Products資料表中生出來了! 接下來在執行時期也可以很放心的不會吐出任何例外訊息因為資料庫異動而要你去 Migrations 了。 我們也可以來順便看看使用 Migrations 時會在 Demo 資料庫中的 Products 資料表的系統資料表產生的 _Migrations 資料表的內容。 我們這個範例中總共做了 Migrations 兩次,一次是新增資料表,一次是新增欄位:

_MigrationTable

嗯~果然在這個資料表中我們也發現了兩筆資料。

小結

在使用 Entity Framework 的 Code First 時對開發人員真的是很方便,以前需要很麻煩的資料庫與程式碼互相切換,還要學會怎麼寫 SQL 這些麻煩事情,使用 Entity Framework 這種 ORM solution 都可以解決掉。而且這次 EF 所提供的 Migrations 功能又可以讓開發人員直接在程式碼中對資料庫異動,完全不需要離開舒適的開發環境。說真的,現在開發要與資料庫互動而且是 MS SQL 那目前的最佳方案我想就是 EF 的 Code First 了!