AspNetBoilerplate抛出异常后访问失败次数未写入数据库问题
问题
基于AspNetBoilerplate框架开发用户验证密码后取消订阅功能时,遇到以下异常情况:
当前实现代码:
[AbpAuthorize] public class UserCompaniesManagerAppService : InvoiceAppServiceBase, IApplicationService { public async Task CancelSubscription(CheckPasswordDto input) { using (CurrentUnitOfWork.SetTenantId(AbpSession.TenantId)) { var user = await GetCurrentUserAsync(); // 验证密码 if (await UserManager.CheckPasswordAsync(user, input.Password)) { await UserManager.ResetAccessFailedCountAsync(user); // TODO: 执行取消订阅逻辑 } else { // 密码错误时记录失败次数 await UserManager.AccessFailedAsync(user); var count = await UserManager.GetAccessFailedCountAsync(user); var maxAttempts = await SettingManager.GetSettingValueAsync<int>( AbpZeroSettingNames.UserManagement.UserLockOut.MaxFailedAccessAttemptsBeforeLockout); throw new UserFriendlyException($"tried {count} left attempts {maxAttempts - count}"); } } } }
抛出UserFriendlyException后,服务器返回的响应格式符合预期,但多次提交错误密码时,失败次数始终保持初始值,且数据库AbpUsers表中未写入失败次数数据。若将方法返回类型改为Task<string>,失败次数统计和账户锁定机制均能正常工作,现需解决抛出异常方式下的次数更新问题。
解决方案
- 手动提交工作单元:抛出异常会直接终止当前工作单元,导致
AccessFailedAsync的更新操作未提交到数据库。在抛出异常前,手动调用CurrentUnitOfWork.SaveChangesAsync()提交变更:else { await UserManager.AccessFailedAsync(user); // 手动提交工作单元,确保失败次数写入数据库 await CurrentUnitOfWork.SaveChangesAsync(); var count = await UserManager.GetAccessFailedCountAsync(user); var maxAttempts = await SettingManager.GetSettingValueAsync<int>( AbpZeroSettingNames.UserManagement.UserLockOut.MaxFailedAccessAttemptsBeforeLockout); throw new UserFriendlyException($"tried {count} left attempts {maxAttempts - count}"); } - 调整工作单元作用域:
using (CurrentUnitOfWork.SetTenantId(...))可能导致工作单元作用域异常,若当前上下文已正确设置租户ID,可尝试移除该语句;若必须保留,需确保所有数据库操作在工作单元内完成并提交后再抛出异常。 - 确认实体状态变更:检查
AccessFailedAsync内部是否正确标记用户实体为修改状态,若未自动标记,可在调用AccessFailedAsync后手动执行await UserManager.UpdateAsync(user),再提交工作单元。
内容的提问来源于stack exchange,提问作者whoiswho
相关产品推荐
相关产品推荐

