EF Code First仅在数据库首次创建时添加参考数据的方法
好问题!EF本身就提供了专门的机制来处理这种仅在数据库首次创建时插入初始/参考数据的场景,完全不用在OnModelCreating里做查询判断,避免额外的数据库调用。下面分两种主流版本(EF6和EF Core)来具体说明:
一、Entity Framework 6.x 实现方案
EF6里最贴合需求的是重写CreateDatabaseIfNotExists初始化器,它的Seed方法只会在数据库首次创建成功后执行一次,后续数据库存在时不会再触发,完美匹配你的需求。
1. 自定义数据库初始化器
创建一个继承自CreateDatabaseIfNotExists的类,在Seed方法里写入你的参考数据:
public class MyDbInitializer : CreateDatabaseIfNotExists<YourDbContext> { protected override void Seed(YourDbContext context) { // 插入PaymentType的参考数据 context.PaymentTypes.AddRange(new List<PaymentType> { new PaymentType { Name = "信用卡" }, new PaymentType { Name = "支付宝" }, new PaymentType { Name = "微信支付" } }); // 保存数据 context.SaveChanges(); base.Seed(context); } }
2. 注册初始化器
在你的DbContext构造函数里注册这个初始化器,或者通过配置文件设置:
public class YourDbContext : DbContext { public YourDbContext() : base("YourConnectionString") { // 设置初始化器 Database.SetInitializer(new MyDbInitializer()); } // 定义实体集合 public DbSet<Invoice> Invoices { get; set; } public DbSet<PaymentType> PaymentTypes { get; set; } }
这样一来,只有当数据库不存在、EF自动创建它之后,Seed里的代码才会跑一次,完全不会有多余的数据库查询。
二、Entity Framework Core 实现方案
EF Core提供了两种更优雅的方式,你可以根据自己的习惯选择:
方式1:使用HasData配置种子数据(官方推荐)
这种方式是在OnModelCreating里直接配置种子数据,EF Core会在数据库创建时自动插入这些数据,后续迁移时只会处理数据的变更(如果有的话),不会重复插入:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置PaymentType的种子数据,注意要指定主键 modelBuilder.Entity<PaymentType>().HasData( new PaymentType { PaymentTypeId = 1, Name = "信用卡" }, new PaymentType { PaymentTypeId = 2, Name = "支付宝" }, new PaymentType { PaymentTypeId = 3, Name = "微信支付" } ); // 其他模型配置逻辑... }
小提示:使用
HasData时必须指定主键,因为EF Core需要靠主键判断数据是否已经存在,避免重复插入。
方式2:手动判断并执行Seed(类似EF6的思路)
如果你更习惯EF6那种手动Seed的方式,也可以在程序启动时处理,比如在.NET 6+的Program.cs里:
var scope = app.Services.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>(); // 仅当数据库不存在时,创建并插入参考数据 if (!dbContext.Database.CanConnect()) { dbContext.Database.EnsureCreated(); SeedReferenceData(dbContext); } // 封装Seed逻辑 void SeedReferenceData(YourDbContext context) { // 双重保险:如果表已经有数据就跳过 if (!context.PaymentTypes.Any()) { context.PaymentTypes.AddRange( new PaymentType { Name = "信用卡" }, new PaymentType { Name = "支付宝" }, new PaymentType { Name = "微信支付" } ); context.SaveChanges(); } }
不过这种方式需要手动判断数据库状态,HasData是EF Core官方更推荐的简洁方案。
为什么不推荐在OnModelCreating里做查询?
你担心的额外数据库调用确实是个问题,而且OnModelCreating的职责是配置模型结构,不是处理数据操作,把插入逻辑放在这里会违反单一职责原则,代码也会更难维护。上面提到的这些官方机制都是专门为初始化数据设计的,完全避免了不必要的查询。
内容的提问来源于stack exchange,提问作者Noobie3001

