ASP.NET Core种子用户添加异常:仅Admin成功,求解决方案及其他方法
问题原因
- 电话号码格式不符合验证规则:从错误截图能看到提示“Invalid PhoneNumber”,第二个用户的
+3801111111长度不足——乌克兰号码标准格式是+380后接10位数字,你第一个用户的号码是合规的,第二个少了一位。 - 同步阻塞异步方法的隐患:用
GetAwaiter().GetResult()强行将异步方法转为同步执行,容易导致EF Core上下文状态混乱,比如刚创建的用户还未同步到数据库,后续直接查询_db.ApplicationUsers可能拿不到数据。 - 用户查询方式不当:创建用户后直接通过
_db.ApplicationUsers查询,而_userManager.CreateAsync已经将用户添加到内存上下文,应该用_userManager.FindByEmailAsync获取用户,避免上下文跟踪导致的查询失效。
解决办法
1. 修正电话号码格式
把第二个用户的PhoneNumber补全为合规长度,比如改为+380111111111。
2. 改用异步写法(推荐)
放弃同步阻塞异步方法的写法,将种子方法改为异步实现,代码更稳定:
public async Task SeedDataAsync() { if (!await _roleManager.RoleExistsAsync(SD.Role_Admin)) { await _roleManager.CreateAsync(new IdentityRole(SD.Role_Admin)); await _roleManager.CreateAsync(new IdentityRole(SD.Role_User)); // 创建Admin用户 var adminCreateResult = await _userManager.CreateAsync(new ApplicationUser { UserName = "admin@gmail.com", Email = "admin@gmail.com", Name = "Admin", PhoneNumber = "+380000000000", StreetAddress = "Street 2", State = "State", PostalCode = "100000", City = "City Name" }, "Admin123*"); if (adminCreateResult.Succeeded) { var adminUser = await _userManager.FindByEmailAsync("admin@gmail.com"); await _userManager.AddToRoleAsync(adminUser, SD.Role_Admin); } // 创建普通用户 var userCreateResult = await _userManager.CreateAsync(new ApplicationUser { UserName = "user@gmail.com", Email = "user@gmail.com", Name = "User", PhoneNumber = "+380111111111", // 修正电话号码 StreetAddress = "Street 2", State = "State", PostalCode = "10000", City = "City Name" // 修正拼写错误:Cuty -> City }, "User123*"); if (userCreateResult.Succeeded) { var regularUser = await _userManager.FindByEmailAsync("user@gmail.com"); await _userManager.AddToRoleAsync(regularUser, SD.Role_User); } } }
3. 检查用户创建结果
每次调用CreateAsync后,检查返回的IdentityResult是否成功,能及时发现验证错误,避免后续代码无效执行。
其他种子用户的实现方式
1. 配置文件批量导入
将用户数据写在JSON配置文件中,批量读取创建,方便后续修改维护:
// 在appsettings.json中添加配置项 "SeedUsers": [ { "UserName": "admin@gmail.com", "Email": "admin@gmail.com", "Name": "Admin", "PhoneNumber": "+380000000000", "Password": "Admin123*", "Role": "Admin" }, { "UserName": "user@gmail.com", "Email": "user@gmail.com", "Name": "User", "PhoneNumber": "+380111111111", "Password": "User123*", "Role": "User" } ]
编写读取配置的种子方法:
public async Task SeedFromConfigAsync(IConfiguration config) { var seedUsers = config.GetSection("SeedUsers").Get<List<SeedUser>>(); foreach (var seedUser in seedUsers) { if (await _userManager.FindByEmailAsync(seedUser.Email) == null) { var appUser = new ApplicationUser { UserName = seedUser.UserName, Email = seedUser.Email, Name = seedUser.Name, PhoneNumber = seedUser.PhoneNumber, StreetAddress = "Street 2", State = "State", PostalCode = "10000", City = "City Name" }; var result = await _userManager.CreateAsync(appUser, seedUser.Password); if (result.Succeeded) { await _userManager.AddToRoleAsync(appUser, seedUser.Role); } } } } // 定义辅助类映射配置数据 public class SeedUser { public string UserName { get; set; } public string Email { get; set; } public string Name { get; set; } public string PhoneNumber { get; set; } public string Password { get; set; } public string Role { get; set; } }
2. EF Core迁移阶段种子数据
如果是数据库初始化的静态数据,可以在OnModelCreating中用HasData配置,需手动处理密码哈希和规范化字段:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 种子角色 var adminRoleId = Guid.NewGuid().ToString(); var userRoleId = Guid.NewGuid().ToString(); modelBuilder.Entity<IdentityRole>().HasData( new IdentityRole { Id = adminRoleId, Name = SD.Role_Admin, NormalizedName = SD.Role_Admin.ToUpper() }, new IdentityRole { Id = userRoleId, Name = SD.Role_User, NormalizedName = SD.Role_User.ToUpper() } ); // 种子Admin用户,手动生成密码哈希 var passwordHasher = new PasswordHasher<ApplicationUser>(); var adminUser = new ApplicationUser { Id = Guid.NewGuid().ToString(), UserName = "admin@gmail.com", NormalizedUserName = "ADMIN@GMAIL.COM", Email = "admin@gmail.com", NormalizedEmail = "ADMIN@GMAIL.COM", Name = "Admin", PhoneNumber = "+380000000000", StreetAddress = "Street 2", State = "State", PostalCode = "100000", City = "City Name", EmailConfirmed = true }; adminUser.PasswordHash = passwordHasher.HashPassword(adminUser, "Admin123*"); modelBuilder.Entity<ApplicationUser>().HasData(adminUser); // 关联用户与角色 modelBuilder.Entity<IdentityUserRole<string>>().HasData( new IdentityUserRole<string> { UserId = adminUser.Id, RoleId = adminRoleId } ); }
这种方式仅在数据库迁移时生效,适合固定的初始数据,不支持动态添加场景。
内容的提问来源于stack exchange,提问作者Oleg Pogorelov
相关产品推荐
相关产品推荐

