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

Ubuntu 20.04 ARM部署.NET程序MySQL用户角色种子数据未生成

故障现象

实现了管理员用户、角色的种子数据初始化逻辑,本地Windows环境连接远程MySQL服务器运行正常,部署到ARM架构Ubuntu 20.04系统后,种子数据始终未创建。
相关实现代码如下:

ContextSeed.cs

public static class ContextSeed
{
    public static async Task SeedRolesAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
    {
        //Seed Roles
        await roleManager.CreateAsync(new IdentityRole(eRoles.SuperAdmin.ToString()));
    }

    public static async Task SeedSuperAdminAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
    {
        //Seed Default User
        var defaultUser = new ApplicationUser
        {
            UserName = "host",
            Email = "xxx@gmail.com",            
            EmailConfirmed = true,
            PhoneNumberConfirmed = true,
            Birthday = new DateTime(1989,10,16)
        };
        if (userManager.Users.All(u => u.Id != defaultUser.Id))
        {
            var user = await userManager.FindByEmailAsync(defaultUser.Email);
            if (user == null)
            {
                await userManager.CreateAsync(defaultUser, "yyy");
                await userManager.AddToRoleAsync(defaultUser, eRoles.SuperAdmin.ToString());
            }

        }
    }
}

Program.cs

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        var context = services.GetRequiredService<ApplicationDbContext>();
        var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
        var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
        await ContextSeed.SeedRolesAsync(userManager, roleManager);
        await ContextSeed.SeedSuperAdminAsync(userManager, roleManager);
    }
    catch (Exception ex)
    {            
        logger.Error(ex, "An error occurred seeding the DB."+ ex.Message);
    }
}
原因排查与修复

代码本身存在两个会导致流程中断的逻辑缺陷,加上跨环境部署的配置差异,共同导致了这个问题,按以下顺序排查修复即可:

  • 角色创建逻辑无幂等性,重复执行直接抛错终止流程
    现有SeedRolesAsync每次启动都会直接执行角色创建操作,只要数据库中已存在同名角色,就会触发唯一键冲突异常,直接被外层catch捕获,后续的用户创建逻辑完全不会执行。本地Windows环境能跑通只是因为本地库是全新空库,第一次执行时角色不存在没有触发报错,不代表逻辑没问题。
    修复代码:
    public static async Task SeedRolesAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
    {
        string roleName = eRoles.SuperAdmin.ToString();
        // 先判断角色是否存在,避免重复创建
        if (!await roleManager.RoleExistsAsync(roleName))
        {
            IdentityResult createRes = await roleManager.CreateAsync(new IdentityRole(roleName));
            if (!createRes.Succeeded)
            {
                throw new Exception($"角色初始化失败:{string.Join(";", createRes.Errors.Select(e => e.Description))}");
            }
        }
    }
    
  • 用户创建判断逻辑无效,角色绑定必然失败
    现有代码里的判断条件userManager.Users.All(u => u.Id != defaultUser.Id)完全无效:手动new出来的defaultUser实例没有赋值Id,默认值是空字符串/空Guid,数据库里的用户Id不可能为空,所以这个判断永远成立,每次启动都会进入分支。更严重的是,调用CreateAsync创建用户后,直接把这个没有Id的本地实例传给了AddToRoleAsync,方法内部需要靠用户Id关联角色数据,空Id必然执行失败。
    修复代码:
    public static async Task SeedSuperAdminAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
    {
        const string adminEmail = "xxx@gmail.com";
        const string adminUser = "host";
        const string adminPwd = "yyy";
        string roleName = eRoles.SuperAdmin.ToString();
    
        // 按邮箱查询用户是否存在,这才是有效判断
        ApplicationUser admin = await userManager.FindByEmailAsync(adminEmail);
        if (admin == null)
        {
            ApplicationUser newAdmin = new ApplicationUser
            {
                UserName = adminUser,
                Email = adminEmail,
                EmailConfirmed = true,
                PhoneNumberConfirmed = true,
                Birthday = new DateTime(1989, 10, 16)
            };
            IdentityResult createRes = await userManager.CreateAsync(newAdmin, adminPwd);
            if (!createRes.Succeeded)
            {
                throw new Exception($"管理员账号创建失败:{string.Join(";", createRes.Errors.Select(e => e.Description))}");
            }
            // 创建完成后重新查库拿到带真实Id的用户实体
            admin = await userManager.FindByEmailAsync(adminEmail);
        }
    
        // 判断用户是否已绑定角色,避免重复添加报错
        if (!await userManager.IsInRoleAsync(admin, roleName))
        {
            IdentityResult bindRes = await userManager.AddToRoleAsync(admin, roleName);
            if (!bindRes.Succeeded)
            {
                throw new Exception($"管理员角色绑定失败:{string.Join(";", bindRes.Errors.Select(e => e.Description))}");
            }
        }
    }
    
  • ARM架构Ubuntu环境专属排查项
    • 先修正日志写法:catch块里不要手动拼接ex.Message,直接传异常对象给日志方法logger.Error(ex, "种子数据执行失败");,这样才能拿到完整的异常堆栈和内部错误信息,不用瞎猜问题。
    • 核对部署后的配置:确认生产环境的MySQL连接字符串、Identity密码规则和本地测试时一致,尤其注意:
      • 连接地址、端口、账号密码不要沿用本地开发配置,确认能从Ubuntu服务器正常连通远程MySQL的3306端口
      • 设置的初始密码yyy不符合Identity默认密码规则(默认要求长度≥6位、包含大小写字母、数字、特殊字符),如果生产环境没关闭密码复杂度校验,创建用户会直接失败
    • 确认数据库迁移已执行:部署后要手动执行dotnet ef database update应用迁移,表结构不存在的话种子逻辑必然失败
    • 检查文件权限:用非root用户运行程序时,要保证运行用户对部署目录、系统SSL证书目录/etc/ssl/certs有读权限,ARM架构下部分MySQL驱动版本会因为读不到SSL证书导致连接失败。

内容的提问来源于stack exchange,提问作者Cuziop

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 03:49:12