EF Core 2.1 終於支援 Data Seeding

當使用 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using Microsoft.EntityFrameworkCore;

namespace EFCoreMigration
{
public class EFLabDbContext: DbContext
{
public DbSet<Customer> 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<Customer>().HasData(new Customer {
Name = "Sam",
Age = 18,
});

modelBuilder.Entity<Customer>().HasData(new Customer {
Name = "Kevin",
Age = 19,
});
}
}
}

15 行

1
2
3
4
5
6
7
8
9
10
11
12
protected override void OnModelCreating(ModelBuilder modelBuilder)
{

modelBuilder.Entity<Customer>().HasData(new Customer {
Name = "Sam",
Age = 18,
});

modelBuilder.Entity<Customer>().HasData(new Customer {
Name = "Kevin",
Age = 19,
});
}

如同設定 connection string 要 override OnConfiguring(),若要使用 Data Seeding 則要 override OnModelCreating()

使用 modelBuilder.Entity<T>().HasData() 新增資料,其中 <T> 為要新增的 Entity 型別。

因為 CustomerID 為 PK,PostgreSQL 會自動處理,所以我們就不特別指定,只設定 NameAge 兩個欄位。

seeding000

建立 Migration


1
~/EFCoreMigration $ dotnet ef migrations add Migration02

因為我們對 DbContext 做了變動,所以要重新建立 Migration。

輸入 dotnet ef migrations add 建立新的 Migration。

seeding001

  1. 建立 Migration 出現錯誤,EF Core 抱怨 CustomerID 沒有提供。

這目前在 .NET Core 2.1 為 Known Issue,當使用 modelBuilder.Entity<T>().HasData() 做 Data Seeding 時,目前連 PK 這種 auto-generated 欄位,也必須手動提供。

EFLabDbContext.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using Microsoft.EntityFrameworkCore;

namespace EFCoreMigration
{
public class EFLabDbContext: DbContext
{
public DbSet<Customer> 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<Customer>().HasData(new Customer {
CustomerID = customerID++,
Name = "Sam",
Age = 18,
});

modelBuilder.Entity<Customer>().HasData(new Customer {
CustomerID = customerID++,
Name = "Kevin",
Age = 19,
});
}
}
}

15 行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected override void OnModelCreating(ModelBuilder modelBuilder)
{

var customerID = 1;

modelBuilder.Entity<Customer>().HasData(new Customer {
CustomerID = customerID++,
Name = "Sam",
Age = 18,
});

modelBuilder.Entity<Customer>().HasData(new Customer {
CustomerID = customerID++,
Name = "Kevin",
Age = 19,
});
}

將 PK 的 CustomerID 加入,並自行使用 customerID++ 處理。

seeding007

1
~/EFCoreMigration $ dotnet ef migrations add Migration02

再重新建立一次 Migration,這次就成功了。

seeding002

Migration02.cs

seeding003

觀察 Migration02.Up(),發現我們剛剛使用 modelBuilder.Entity<T>().HasData() 新增的資料,已經成為 Migration 的一部分。

ModelSnapshot.cs

seeding004

觀察 ModelSnapshot.cs,發現我們剛剛使用 modelBuilder.Entity<T>().HasData() 新增的資料也寫入了 ModelSnapshot.cs,因此之後再建立新的 Migration 時,就有了 golden sample 可以比對,不會重複新增 Data Seeding 資料。

執行 Migration


1
~/EFCoreMigration $ dotnet ef database update

輸入 dotnet ef database update 執行 Migration。

seeding005

  1. 只執行了 Migration02

確認資料


seeding006

  • 兩筆資料已經透過 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’.

2018-06-14