ASP.NET MVC 使用 Entity Framework Code First
2022-12-10
筆記如何使用 Entity Framework 在 ASP.NET MVC 專案下使用 Code First 進行開發。
說明
安裝 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
- 預設的行為,當資料庫不存在時建立,EF 1.0 加入。
- DropCreateDatabaseAlways
- 每次重新啟動 IISExpress 時,自動重新建立資料庫,適合使用在模擬與測試環境,EF 1.0 加入。
- DropCreateDatabaseIfModelChanges
- 當模型 (POCO 類別) 發生改變時,自動重新建立資料庫,適合在設計類別的開發情境,EF 4.1 加入
上述的 Initialization Strategies 最大的限制就是可能誤對正式環境的資料造成影響,因此在 EF 4.3 加入的 Migrations成為了資料庫異動的最優先適用技術,能夠使用類似於 Git 的方式達到對資料庫的異動版本控制化,並且更具資料庫異動上的彈性。
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 Migrations 以及 Code-Based Migrations。
Auto Migrations
使用 Auto Migrations 後,當 POCO 以及 Model 發生變化時,會自動進行資料庫綱要的變更,同時會在資料庫的 __MigrationHistory
資料表加上紀錄。
注意要使用的是 Nuget 套件管理員中的「套件管理主控台」,而非使用「開發人員 PowerShell」,否則會發生「 無法辨識 'enable-migration' 詞彙是否為 Cmdlet、函數、指令檔或可執行程式的名稱。請檢查名稱拼字是否正確,如果包含路徑的話,請確認路徑是否正確,然後再試一次。」的問題 😑
要使用 Auto Migrations 需要在 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
Update-Database
Update-Database -Script -SourceMigration:0
參考資料
[料理佳餚] Entity Framework Code First 不算太難用 | 軟體主廚的程式料理廚房
Creating an Entity Framework Data Model for an ASP.NET MVC Application