重写返回Task的方法添加async时出现StackOverflow异常
解决ASP.NET Identity 2.2.1中重写FindByIdAsync导致的StackOverflow异常
你遇到的栈溢出问题根源很明确:无限递归调用。
为什么会出现递归?
默认的UserManager.GetRolesAsync()方法内部逻辑会先调用FindByIdAsync()来确认用户的有效性——而你重写的FindByIdAsync()又调用了this.GetRolesAsync(),这就形成了一个死循环:MyUserManager.FindByIdAsync() → base.FindByIdAsync() → this.GetRolesAsync() → 内部触发FindByIdAsync() → 回到你的重写方法,反复调用直到栈被撑爆。
解决方案:绕过UserManager的GetRolesAsync,直接查询角色数据
要避免递归,我们需要跳过UserManager封装的GetRolesAsync(),直接从底层数据存储(比如EF的DbContext)查询用户的角色信息。
方法1:直接通过DbContext查询(推荐)
如果你的项目使用Entity Framework作为存储,直接注入DbContext并查询用户角色关联表:
public class MyUserManager : UserManager<MyUser>, IMyUserManager { private readonly ApplicationDbContext _dbContext; // 构造函数注入DbContext和其他必要依赖 public MyUserManager(IUserStore<MyUser> userStore, ApplicationDbContext dbContext, IPasswordHasher<MyUser> passwordHasher, /* 其他需要的服务 */) : base(userStore) { _dbContext = dbContext; PasswordHasher = passwordHasher; // 其他初始化配置... } public override async Task<MyUser> FindByIdAsync(string userId) { var user = await base.FindByIdAsync(userId); if (user == null) { return null; } // 直接从数据库查询用户的角色名称,跳过UserManager的GetRolesAsync user.RoleNames = await _dbContext.UserRoles .Where(ur => ur.UserId == userId) .Join(_dbContext.Roles, ur => ur.RoleId, r => r.Id, (ur, r) => r.Name) .ToListAsync(); return user; } }
方法2:通过RoleStore查询
如果你更倾向于使用Identity的Store接口而非直接操作DbContext,可以注入IRoleStore来查询角色:
public class MyUserManager : UserManager<MyUser>, IMyUserManager { private readonly IRoleStore<IdentityRole, string> _roleStore; private readonly IUserRoleStore<MyUser, string> _userRoleStore; public MyUserManager(IUserStore<MyUser> userStore, IRoleStore<IdentityRole, string> roleStore, /* 其他依赖 */) : base(userStore) { _roleStore = roleStore; _userRoleStore = userStore as IUserRoleStore<MyUser, string>; // 初始化配置... } public override async Task<MyUser> FindByIdAsync(string userId) { var user = await base.FindByIdAsync(userId); if (user == null) { return null; } // 获取用户关联的角色ID集合 var roleIds = await _userRoleStore.GetRolesAsync(user); // 批量查询角色实体并提取名称 var roles = new List<IdentityRole>(); foreach (var roleId in roleIds) { var role = await _roleStore.FindByIdAsync(roleId); if (role != null) { roles.Add(role); } } user.RoleNames = roles.Select(r => r.Name).ToList(); return user; } }
注意事项
- 确保DI容器正确注入所有需要的依赖(DbContext、各种Store等),避免出现注入错误。
- 如果你使用的是自定义的
IdentityRole或IdentityUserRole,需要调整代码中的泛型类型和查询逻辑,匹配你的实体定义。
内容的提问来源于stack exchange,提问作者Bartosz
相关产品推荐
相关产品推荐

