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证书导致连接失败。
- 先修正日志写法:catch块里不要手动拼接
内容的提问来源于stack exchange,提问作者Cuziop
相关产品推荐
相关产品推荐

