HasData khai báo dữ liệu ngay trong model, và EF đưa nó vào migration dưới dạng InsertData/UpdateData:
protected override void OnModelCreating(ModelBuilder b)
{
b.Entity<Role>().HasData(
new Role { Id = 1, Code = "ADMIN" },
new Role { Id = 2, Code = "USER" });
}EF so sánh dữ liệu seed giữa hai lần migration để sinh insert/update/delete tương ứng — nghĩa là seed cũng được version hoá cùng schema.
Hạn chế của HasData:
- Bắt buộc chỉ định primary key tường minh, kể cả khi khoá là identity, vì EF cần biết dòng nào để update/delete ở migration sau.
- Không dùng được navigation property; phải gán trực tiếp giá trị khoá ngoại.
- Chỉ hợp dữ liệu tĩnh: không gọi được service, không hash password, không DateTime.Now (giá trị sẽ bị bake cứng vào file migration).
Từ EF Core 9, UseSeeding / UseAsyncSeeding (cấu hình trên DbContextOptionsBuilder) là cách được khuyến nghị cho seed động — code chạy khi khởi tạo context, gọi được DI, và được bảo vệ bởi cơ chế khoá migration nên nhiều instance khởi động cùng lúc không chèn trùng.
Thực tế hay dùng cả hai: HasData cho bảng tra cứu bất biến, UseAsyncSeeding cho dữ liệu cần logic.