ASP.NET MVC 使用 Entity Framework Code First


  1. 說明
    1. 安裝 Entity Framework
    2. 建立 POCO 類別
    3. 建立 DBContext
    4. DatabaseCreate & Database Initialization Strategies
    5. Migrations
      1. Auto Migration
      2. Code-Based Migration
  2. 參考資料

筆記如何使用 Entity Framework 在 ASP.NET MVC 專案下使用 Code First 進行開發。

logo

說明

安裝 Entity Framework

Install-Package EntityFramework

建立 POCO 類別

namespace EF_CodeFirst.Models
{
    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime? BirthDate { get; set; }
    }
}

建立 DBContext

SqlDbContext.cs

using System.Data.Entity;
using EF_CodeFirst.Models;

namespace EF_CodeFirst.DAL
{
    public class SqlDbContext : DbContext
    {
        public SqlDbContext() : base()
        {
        }
        public DbSet<Person> People { get; set; }
    }
}

完成上述的設定,Entity Framework 就已經能夠被啟動了,而這個時候資料庫會被建立在那裡?

預設上當 DbContext 的建構子繼承沒有提供任何參數時,預設就會以 namespace 命名資料庫,並且建立在 (LocalDB)\MSSQLLocalDB 當中。

而如果想要指定資料庫名稱,可以藉由提供建構子參數來達成:

public class SqlDbContext : DbContext
{
    public SqlDbContext() : base("DatabaseName")
    {
    }
    public DbSet<Person> People { get; set; }
}

此外也可以指定為 Web.config 當中的連線字串名稱或者是直接提供連線字串的方式,讓 Entity Framework 直接在目標資料庫伺服器進行資料庫的建立:

Web.config

<connectionStrings>
  <add name="CodeFirstDB"
        connectionString="Data Source=.;Initial Catalog=DabaseName;Integrated Security=True;
        Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;
        ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
        providerName="System.Data.SqlClient" />
</connectionStrings>

SqlDbContext.cs

public class SqlDbContext : DbContext
{
    public SqlDbContext() : base("name=CodeFirstDB")
    {
    }
    public DbSet<Person> People { get; set; }
}

DatabaseCreate & Database Initialization Strategies

HomeController.cs

public class HomeController : Controller
{
    private SqlDbContext context = new SqlDbContext();
    public ActionResult Index()
    {
        return View(context.People.ToList());
    }

    public ActionResult AddPerson()
    {
        context.People.Add(new Person { Name = "Alice" });
        context.SaveChanges();
        return RedirectToAction("Index");
    }
}

在 IISExpress 啟動後,資料庫就會自行建立,配合 Controller 可以測試資料的呈現。

而如果 POCO 類別發生改變,預設上資料庫只會在不存在時重建,因此 POCO 類別的改變不會讓資料庫的綱要重新建立。

這時可以搭配 Database.SetInitializer 來指定資料庫初始化的策略。

CreateDatabaseIfNotExists
預設的行為,當資料庫不存在時建立
DropCreateDatabaseAlways
每次重新啟動 IISExpress 時,自動重新建立資料庫,適合使用在模擬與測試環境
DropCreateDatabaseIfModelChanges
當模型 (POCO 類別) 發生改變時,自動重新建立資料庫,適合在設計類別的開發情境

SqlDbContext.cs

public class SqlDbContext : DbContext
{
    public SqlDbContext() : base("name=CodeFirstDB")
    {
        Database.SetInitializer<SqlDbContext>(new DropCreateDatabaseAlways<SqlDbContext>());
        Database.SetInitializer<SqlDbContext>(new CreateDatabaseIfNotExists<SqlDbContext>());
        Database.SetInitializer<SqlDbContext>(new DropCreateDatabaseIfModelChanges<SqlDbContext>());
    }
    public DbSet<Person> People { get; set; }
}

此外也能基於上述的資料庫初始化策略,客製化資料庫初始化,結合自動加入測試資料的應用方式:

SqlDbContext.cs

public class SqlDbContext : DbContext
{
    public SqlDbContext() : base("name=CodeFirstDB")
    {
            Database.SetInitializer<SqlDbContext>(new Initializer());

    }
    public DbSet<Person> People { get; set; }
}

Initializer.cs

public class Initializer : DropCreateDatabaseIfModelChanges<SqlDbContext>
{
    protected override void Seed(SqlDbContext context)
    {
        base.Seed(context);

        context.People.Add(
          new Person { Name = "Alice", BirthDate = new System.DateTime(1995, 2, 1) });
        context.People.Add(
          new Person { Name = "Bob", BirthDate = new System.DateTime(1995, 12, 5) });

        context.SaveChanges();

        Console.WriteLine("Database Initialized Done.");
    }
}

上述填入初始資料的動作稱為 Database seeding

Migrations

而雖然有了資料庫初始化策略,但隨著開發過程的深入,使用者可能在資料庫已經留下許多的測試資料,不適合再貿然重置整個資料庫,這個時候就可以加入 migrations 機制,為資料庫的異動建立版本控制,兼顧開發過程中,資料庫的升版與退版功能。

而在 Migrations 的使用,可以分為 Auto Migration 以及 Code-Based Migration。

Auto Migration

使用 Auto Migration 後,當 POCO 以及 Model 發生變化時,會自動進行資料庫綱要的變更,同時會在資料庫的 __MigrationHistory 資料表加上紀錄。

要使用 Auto Migration 需要在 Package Manager Console 執行 Enable-Migrations

Enable-Migrations -EnableAutomaticMigration:$true

執行後,會自動加入 Migrations 資料夾以及 COnfiguration.cs

Migrations/Configuration.cs

internal sealed class Configuration : 
  DbMigrationsConfiguration<EF_CodeFirst.DAL.SqlDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
        ContextKey = "EF_CodeFirst.DAL.SqlDbContext";
        AutomaticMigrationDataLossAllowed = true;
    }

    protected override void Seed(EF_CodeFirst.DAL.SqlDbContext context)
    {
        ...
    }
}

預設不會加入 AutomaticMigrationDataLossAllowed,啟用才能夠允許當 POCO 變更如果會造成資料移除的情境。

需要注意的是當使用 Auto Migratinos 後,雖然只有在POCO 以及 Model 發生變化時,才會在 __MigrationHistory 加入異動紀錄,但 Seed 的部分則是每次應用程式啟用都會被執行。因此如果有 Seeding Database 的需求,需要在 Seed 的部分檢查資料是否已經存在,而非直接重複性的加入資料,詳細可以參考 Stackoverflow 的討論 Confusion over EF Auto Migrations and seeding - seeding every program start

Code-Based Migration

enable-migrations
add-migrations -name Description
update-database

參考資料

Entity Framework Tutorial

[料理佳餚] Entity Framework Code First 不算太難用 | 軟體主廚的程式料理廚房

Creating an Entity Framework Data Model for an ASP.NET MVC Application