向已存在角色添加用户时任务被取消的问题排查
问题排查:用户分配角色时出现任务取消错误
现有数据
角色表
| Role Id | Name | NormalizedName |
|---|---|---|
| 59f5238-818e-40f9-b95a-fc65db67f253 | Parent | Parent |
用户表
| Id | First Name | Surname |
|---|---|---|
| b3e9d5ec-1ddf-4e57-bf00-1afc2e7f0582 | Parent | One |
错误信息
The Task is cancelled
at
Microsoft.AspNetCore.Identity.UserManager`1.d__116.MoveNext()
at Web.SampleData.d__2.MoveNext() in
SampleData.cs:line 118
相关代码
分配角色方法
public static async Task<IdentityResult> AssignRoles(IServiceProvider services, string email, string[] roles) { IdentityResult result = new IdentityResult(); UserManager<ApplicationUser> _userManager = services.GetService<UserManager<ApplicationUser>>(); ApplicationUser user = await _userManager.FindByEmailAsync(email); result = await _userManager.AddToRolesAsync(user, roles); return result; }
完整种子数据初始化方法
public static void CreateParent(IServiceProvider serviceProvider) { var context = new DBContext();// serviceProvider.GetService<DBContext>(); string[] roles = new string[] { "Parent" }; foreach (string role in roles) { var roleStore = new RoleStore<IdentityRole>(context); if (!context.Roles.Any(r => r.Name == role)) { roleStore.CreateAsync(new IdentityRole(role)); } } var user = new ApplicationUser { FirstName = "Parent", LastName = "One", Email = "parentone@apps-Test.com", NormalizedEmail = "parentone@apps-Test.com", UserName = "parentone@apps-Test.com", NormalizedUserName = "parentone@apps-Test.com", PhoneNumber = "+111111111111", EmailConfirmed = true, PhoneNumberConfirmed = true, SecurityStamp = Guid.NewGuid().ToString("D") }; var db = new DBContext(); if (!db.Users.Any(u => u.UserName == user.UserName)) { var password = new PasswordHasher<ApplicationUser>(); var hashed = password.HashPassword(user, "Test12345!"); user.PasswordHash = hashed; var userStore = new UserStore<ApplicationUser> (context); var result = userStore.CreateAsync(user); } AssignRoles(serviceProvider, user.Email, roles); db.SaveChangesAsync(); }
Startup.cs调用代码
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IServiceProvider service) { SampleData.CreateParent(service); }
角色已成功创建,但将用户添加到角色时出现任务取消错误,用户未被添加到aspnetuseroles表,请求排查原因。
问题原因及解决方法
核心问题:异步方法未正确等待
所有调用的异步方法都未使用await关键字,导致任务在未完成时就被终止,触发任务取消:
roleStore.CreateAsync(new IdentityRole(role)):创建角色的异步操作未等待,可能角色还未写入数据库就执行后续步骤userStore.CreateAsync(user):创建用户的异步操作未等待,用户可能未持久化就尝试分配角色AssignRoles(serviceProvider, user.Email, roles):分配角色的异步方法被当作同步方法调用,未等待完成db.SaveChangesAsync():保存更改的异步操作未等待,数据未持久化就结束流程
次要问题:DbContext实例混乱
代码中多次自行创建DBContext实例(new DBContext()),而非从依赖注入获取,导致上下文状态不一致:
- 创建用户用的是新实例
db,但写入用户用的是另一个实例context - 不同上下文实例的操作无法共享状态,可能导致查询不到刚创建的用户/角色
解决步骤
1. 将种子方法改为异步,并正确等待所有异步操作
public static async Task CreateParentAsync(IServiceProvider serviceProvider) { // 从DI获取DbContext,不要自行new var context = serviceProvider.GetService<DBContext>(); string[] roles = new string[] { "Parent" }; foreach (string role in roles) { var roleStore = new RoleStore<IdentityRole>(context); if (!context.Roles.Any(r => r.Name == role)) { // 等待角色创建完成 await roleStore.CreateAsync(new IdentityRole(role)); } } var user = new ApplicationUser { FirstName = "Parent", LastName = "One", Email = "parentone@apps-Test.com", NormalizedEmail = "PARENTONE@APPS-TEST.COM", // 符合Identity默认大写规范化逻辑 UserName = "parentone@apps-Test.com", NormalizedUserName = "PARENTONE@APPS-TEST.COM", PhoneNumber = "+111111111111", EmailConfirmed = true, PhoneNumberConfirmed = true, SecurityStamp = Guid.NewGuid().ToString("D") }; // 使用同一个context实例查询用户 if (!context.Users.Any(u => u.UserName == user.UserName)) { var password = new PasswordHasher<ApplicationUser>(); var hashed = password.HashPassword(user, "Test12345!"); user.PasswordHash = hashed; var userStore = new UserStore<ApplicationUser>(context); // 等待用户创建完成 await userStore.CreateAsync(user); } // 等待角色分配完成 await AssignRoles(serviceProvider, user.Email, roles); // 等待保存更改完成 await context.SaveChangesAsync(); }
2. 修改Startup.cs中的调用方式
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider service) { // 异步执行种子数据,避免异步上下文问题 SampleData.CreateParentAsync(service).GetAwaiter().GetResult(); // 其他中间件配置... }
3. 修正规范化名称
Identity默认会将NormalizedEmail和NormalizedUserName转为大写,手动设置时保持大写,避免后续查询(比如FindByEmailAsync)因大小写不匹配找不到用户。
内容的提问来源于stack exchange,提问作者dotnetdevcsharp
相关产品推荐
相关产品推荐

