You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core种子用户添加异常:仅Admin成功,求解决方案及其他方法

问题原因
  1. 电话号码格式不符合验证规则:从错误截图能看到提示“Invalid PhoneNumber”,第二个用户的+3801111111长度不足——乌克兰号码标准格式是+380后接10位数字,你第一个用户的号码是合规的,第二个少了一位。
  2. 同步阻塞异步方法的隐患:用GetAwaiter().GetResult()强行将异步方法转为同步执行,容易导致EF Core上下文状态混乱,比如刚创建的用户还未同步到数据库,后续直接查询_db.ApplicationUsers可能拿不到数据。
  3. 用户查询方式不当:创建用户后直接通过_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 16:00:59