當使用 Code First 與 Migration 後,下一步就是 Data Seeding,讓我們對 table 新增基本的資料。在 EF Core 2.0,Data Seeding 只能自己手動處理,在 EF Core 2.1 正式提供 Data Seeding。
Version
macOS High Sierra 10.13.4
Docker for Mac 18.03-ce-mac65 (24312)
.NET Core 2.1
Entity Framework 2.1
PostgreSQL 10.3
Npgsql EF Core Provider 2.1
VS Code 1.24.0
DataGrip 2018.4
建立資料
我們可以將一些 database 預設的資料寫在 DbContext.OnModelCreating()
,這樣在 Migration 時,就會順便將資料寫進 database。 1 1 本文為 如何在 Entity Framework Core 使用 Migration ? (PostgreSQL) 內容之延續,請搭配參考
EFLabDbContext.cs
using Microsoft.EntityFrameworkCore;
namespace EFCoreMigration
{
public class EFLabDbContext: DbContext
{
public DbSet Customers { get; set; }
private const string DbCOnnectionString= "Host=localhost;Port=5432;Database=eflab;Username=admin;Password=12345";
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql(DbConnectionString);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity().HasData(new Customer {
Name = "Sam",
Age = 18,
});
modelBuilder.Entity().HasData(new Customer {
Name = "Kevin",
Age = 19,
});
}
}
}
15 行
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity().HasData(new Customer {
Name = "Sam",
Age = 18,
});
modelBuilder.Entity().HasData(new Customer {
Name = "Kevin",
Age = 19,
});
}
如同設定 connection string 要 override OnConfiguring()
,若要使用 Data Seeding 則要 override OnModelCreating()
。
使用 modelBuilder.Entity().HasData()
新增資料,其中
為要新增的 Entity 型別。
因為 CustomerID
為 PK,PostgreSQL 會自動處理,所以我們就不特別指定,只設定 Name
與 Age
兩個欄位。
建立 Migration
~/EFCoreMigration $ dotnet ef migrations add Migration02
因為我們對 DbContext 做了變動,所以要重新建立 Migration。
輸入 dotnet ef migrations add
建立新的 Migration。
- 建立 Migration 出現錯誤,EF Core 抱怨
CustomerID
沒有提供。
這目前在 .NET Core 2.1 為 Known Issue ,當使用 modelBuilder.Entity().HasData()
做 Data Seeding 時,目前連 PK 這種 auto-generated 欄位,也必須手動提供。
EFLabDbContext.cs
using Microsoft.EntityFrameworkCore;
namespace EFCoreMigration
{
public class EFLabDbContext: DbContext
{
public DbSet Customers { get; set; }
private const string DbCOnnectionString= "Host=localhost;Port=5432;Database=eflab;Username=admin;Password=12345";
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql(DbConnectionString);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var customerID = 1;
modelBuilder.Entity().HasData(new Customer {
CustomerID = customerID++,
Name = "Sam",
Age = 18,
});
modelBuilder.Entity().HasData(new Customer {
CustomerID = customerID++,
Name = "Kevin",
Age = 19,
});
}
}
}
15 行
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var customerID = 1;
modelBuilder.Entity().HasData(new Customer {
CustomerID = customerID++,
Name = "Sam",
Age = 18,
});
modelBuilder.Entity().HasData(new Customer {
CustomerID = customerID++,
Name = "Kevin",
Age = 19,
});
}
將 PK 的 CustomerID
加入,並自行使用 customerID++
處理。
~/EFCoreMigration $ dotnet ef migrations add Migration02
再重新建立一次 Migration,這次就成功了。
Migration02.cs
觀察 Migration02.Up()
,發現我們剛剛使用 modelBuilder.Entity().HasData()
新增的資料,已經成為 Migration 的一部分。
ModelSnapshot.cs
觀察 ModelSnapshot.cs
,發現我們剛剛使用 modelBuilder.Entity().HasData()
新增的資料也寫入了 ModelSnapshot.cs
,因此之後再建立新的 Migration 時,就有了 golden sample 可以比對,不會重複新增 Data Seeding 資料。
執行 Migration
~/EFCoreMigration $ dotnet ef database update
輸入 dotnet ef database update
執行 Migration。
- 只執行了
Migration02
確認資料
- 兩筆資料已經透過 Data Seeding 新增至 database
Conclusion
- 理論上在 Data Seeding 時,PK 欄位應該要省略,但目前 EF Core 2.1 的
HasData()
仍必須自己處理 PK 欄位,是比較可惜的地方
- Data Seeding 最大的用處在於使用 Docker 的
整合測試
,當一個測試案例執行時,PostgreSQL 隨著 docker-compose up -d
而跑起來,此時 database 是空的,必須重新執行 Migration 與 Data Seeding,將 schema 與基本資料建立起來,然後才能讓每個測試案例新增測試資料跑測試
- Data Seeding 也可以用在 production 環境,當 production 環境需要一些基本資料才能正常執行時,就適合使用 Data Seeding
Sample Code
完整的範例可以在我的 GitHub 上找到
Reference
Microsoft Docs , Data Seeding
Entity Framework Core , Seeding data: The seed entity for entity type ‘X’ cannot be added because there was no value provided for the required property ‘Id’.