ASP.NET Core添加迁移时如何仅执行一次种子数据初始化
解决ASP.NET Core Web Api迁移时种子数据重复插入的问题
当前代码的问题
你当前的写法存在两个核心错误:
EntityTypeBuilder<AppRole>.Configure是EF Core迁移设计时执行的方法,不能在这里直接通过_roleManager访问数据库(这是运行时操作),也不能使用async void——设计时环境不支持异步操作。- 每次调用
Guid.NewGuid()生成新ID,会导致EF Core每次迁移都认为是新数据,从而重复生成插入语句。
两种可行的解决方案
方案一:使用固定ID的迁移种子数据(适合静态基础数据)
给种子数据指定固定的ID,EF Core会自动检查这些ID是否存在,仅首次迁移时插入,后续迁移不会重复操作。
public void Configure(EntityTypeBuilder<AppRole> builder) { // 提前定义固定的Guid,避免每次迁移生成新ID const string SuperUserId = "12345678-1234-1234-1234-1234567890AB"; const string AdminId = "87654321-4321-4321-4321-BA0987654321"; const string UserId = "11111111-2222-3333-4444-555555555555"; builder.HasData( new AppRole { Id = SuperUserId, Name = "super user", NormalizedName = "SUPER USER" }, new AppRole { Id = AdminId, Name = "admin", NormalizedName = "ADMIN" }, new AppRole { Id = UserId, Name = "user", NormalizedName = "USER" } ); }
原理:EF Core在生成迁移脚本时,会基于固定ID判断数据是否已存在,仅当数据库中无对应ID的记录时才生成插入语句,后续迁移不会重复执行。
方案二:运行时初始化种子数据(适合动态检查场景)
不在迁移逻辑中处理,而是在程序启动时检查数据是否存在,不存在则插入,完全脱离迁移流程。
// Program.cs var app = builder.Build(); // 初始化种子数据 using var scope = app.Services.CreateScope(); var services = scope.ServiceProvider; var roleManager = services.GetRequiredService<RoleManager<AppRole>>(); var roleList = new List<(string Name, string NormalizedName)> { ("super user", "SUPER USER"), ("admin", "ADMIN"), ("user", "USER") }; foreach (var role in roleList) { if (!await roleManager.RoleExistsAsync(role.NormalizedName)) { await roleManager.CreateAsync(new AppRole { Name = role.Name, NormalizedName = role.NormalizedName }); } } // 后续中间件配置 app.UseAuthorization(); app.MapControllers(); app.Run();
原理:程序启动时从DI容器获取RoleManager,逐个检查角色是否存在,仅当不存在时才创建。这种方式与迁移操作完全无关,无论执行多少次迁移,都只会在程序启动时做一次检查。
方案选择建议
- 若为不会变更的静态基础数据(如角色、系统权限),优先选方案一,数据会随迁移脚本自动初始化,适合新环境部署场景。
- 若为需要动态判断或易变更的数据,选方案二,灵活性更高,不受迁移流程限制。
内容的提问来源于stack exchange,提问作者kaan240
相关产品推荐
相关产品推荐

