Entity Framework Core 数据种子设定
种子数据为什么要指定主键的值?
因为在团队开发时,这样可以确保不同的开发人员、电脑、服务器上,在同一个迁移版本具有相同的种子数据。
为什要用种子数据库 Data-Seeding?
在efcore2.0前,如果要在新表或系统初始化插入一些数据,往往是需要写sql代码。这样会造成脚本代码的编写及脚本代码的维护和版本的问题,也使得这些数据和代码是隔离托管的,容易造成不一致性。
Data-Seeding是EntityFrameworkCore 2.1以上版本新增加的特性。在项目刚开始的时候,我们往往是需要初始化一些基础数据到数据库中,通过Data-Seeding特性就可以实现这一功能。
简而言之:数据种子设定是使用初始数据集填充数据库的过程。
初始化的方法
具体的数据初始化方法分为如下三种:
为种子数据建模,模型中配置。这种是通过调用HasData()方法。
手动迁移时添加。
自定义初始化逻辑。
为种子数据建模
与 EF6 不同,在 EF Core 中,种子设定数据可以在模型配置中与实体类型相关联。 随后,将数据库升级为新版本模型时,EF Core 迁移可以自动计算需要应用的插入、更新或删除操作。
例如,这将在 OnModelCreating 中为 Blog 配置种子数据:
modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Url = "http://sample.com" });
若要添加具有关系的实体,需要指定外键值:
modelBuilder.Entity<Post>().HasData(
new Post { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });
如果实体类型具有处于阴影状态的任何属性,可以使用匿名类来提供值:
odelBuilder.Entity<Post>().HasData(
new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" });
从属实体类型可以通过类似的方式进行种子设定:
modelBuilder.Entity<Post>().OwnsOne(p => p.AuthorName).HasData(
new { PostId = 1, First = "Andriy", Last = "Svyryd" },
new { PostId = 2, First = "Diego", Last = "Vega" });
或者,可以使用 context.Database.EnsureCreated() 创建包含种子数据的新数据库,例如,用于测试数据库,或者使用内存中提供程序或任何非关系数据库时。 请注意,如果数据库已存在,EnsureCreated() 既不会更新架构,也不会在数据库中设定数据种子。 对于关系数据库,如果计划使用迁移,则不应调用 EnsureCreated()。
为种子数据建模的限制
此类种子数据由迁移管理,更新数据库中已有数据的脚本需要在不连接到数据库的情况下生成。 这施加了一些限制:
- 即使主键值通常由数据库生成,也需要指定它。 它将用于检测迁移之间的数据更改。
- 如果以任何方式更改主键,将删除之前设定种子的数据。
因此,此功能最适用于不应在迁移外部更改且不依赖于数据库中任何其他内容(例如邮政编码)的静态数据。
如果场景包括以下任一项,建议使用上一部分所述的自定义初始化逻辑: - 用于测试的临时数据
- 依赖于数据库状态的数据
- 数据量较大(种子设定数据在迁移快照中捕获,而数据量较大可能会快速导致大型文件和性能下降)。
- 需要由数据库生成键值的数据,包括使用备用键作为标识的实体
- 需要自定义转换(不由值转换处理)的数据,例如某些密码哈希
- 需要调用外部 API 的数据,例如 ASP.NET Core 标识角色和用户创建
手动迁移自定义
添加迁移时,对使用 HasData 指定的数据所做的更改将转换为对 InsertData()、UpdateData() 和 DeleteData() 的调用。 解决 HasData 的某些限制的方法之一是改为手动向迁移添加这些调用或HasData。
migrationBuilder.InsertData(
table: "Blogs",
columns: new[] { "Url" },
values: new object[] { "http://generated.com" });
自定义初始化逻辑
执行数据种子设定的一种简单而强大的方法,是在主应用程序逻辑开始执行之前使用 DbContext.SaveChanges()。
using (var context = new DataSeedingContext())
{
context.Database.EnsureCreated();
var testBlog = context.Blogs.FirstOrDefault(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Blogs.Add(new Blog { Url = "http://test.com" });
}
context.SaveChanges();
}
种子设定代码不应是正常应用执行的一部分,因为当多个实例正在运行时,这可能会导致并发问题,并且还需要应用有权修改数据库架构。
根据部署的约束,初始化代码可以通过不同的方式执行:
在本地运行初始化应用
使用主应用部署初始化应用,调用初始化例程并禁用或删除初始化应用。
这通常可以通过使用发布配置文件自动执行。
用于 ASP.NET Core 应用部署的 Visual Studio 发布配置文件 (.pubxml)
用于 ASP.NET Core 应用部署的 Visual Studio 发布配置文件 (.pubxml)
参考:数据种子设定