如何解决ASP.NET Core Web API中TransactionScope已完成的错误
问题原因与修复方案
错误“The current TransactionScope is already complete”的核心原因有两个:
- 手动调用
transaction.Dispose()提前终止了事务,后续代码(或框架内部)的数据库操作尝试使用已结束的事务 - 登录操作
SignInAsync被放在transaction.Complete()之后,若该操作触发EF Core查询,就会访问已完成的事务
具体修复步骤:
移除所有手动的
transaction.Dispose()调用using语句会自动管理TransactionScope的生命周期,手动调用Dispose()会提前释放事务,导致后续数据库操作报错。将登录操作移到事务范围外
用户登录属于会话管理操作,和数据库事务无关,不需要包含在事务里。事务只需包裹用户创建、角色分配、BankUser插入这些数据库变更操作。
修改后的代码示例:
public async Task<Response<string>> RegisterUserAsync(string userId) { var response = new Response<string>(); using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { try { var userDetail = _dataAccess.GetSaffUserProfile(userId); adminAccess = _config.GetSection("DDMAdminCredentials").GetValue<string>("adminaccess"); if (string.IsNullOrEmpty(userDetail.user_logon_name)) { _logger.Error("Failed Authentication"); response.Successful = false; response.StatusCode = (int)HttpStatusCode.BadRequest; response.Message = "Staff Record Not Found!"; return response; } var user = new ApplicationUser() { UserName = userDetail.user_logon_name.ToLower().Trim(), Email = string.IsNullOrWhiteSpace(userDetail.email) ? null : userDetail.email.Trim().ToLower(), FirstName = string.IsNullOrWhiteSpace(userDetail.firstname) ? null : userDetail.firstname.Trim(), LastName = string.IsNullOrWhiteSpace(userDetail.lastname) ? null : userDetail.lastname.Trim(), MobileNumber = string.IsNullOrWhiteSpace(userDetail.mobile_phone) ? null : userDetail.mobile_phone.Trim(), CreatedAt = DateTime.Now }; var result = await _userManager.CreateAsync(user, adminAccess); if (result.Succeeded) { // Insert User Role await _userManager.AddToRoleAsync(user, userRole); userBranchNo = userDetail.branch_code.TrimStart('0'); var bankBranchId = _dbContext.BankBranches.Where(u => u.BranchNumber.ToString() == userBranchNo.ToString()).Select(m => m.Id).FirstOrDefault(); var bankUser = new BankUser() { UserId = user.Id, BankBranchId = bankBranchId, CreatedBy = "System" }; await _unitOfWork.AdminUsers.InsertAsync(bankUser); await _unitOfWork.Save(); response.StatusCode = (int)HttpStatusCode.Created; response.Successful = true; response.Data = user.Id; response.Message = "Bank User Created Successfully!"; transaction.Complete(); return response; } response.Message = GetErrors(result); response.StatusCode = (int)HttpStatusCode.BadRequest; response.Successful = false; return response; } catch (Exception ex) { response.Message = "An error occured :" + ex.Message; response.Successful = false; response.StatusCode = (int)HttpStatusCode.BadRequest; return response; } } // 登录操作移到事务范围外 var createdUser = await _userManager.FindByIdAsync(response.Data); if(createdUser != null) { await _signInManager.SignInAsync(createdUser, isPersistent: false); } return response; }
额外说明:
- 事务完成后重新通过
FindByIdAsync获取用户,是因为原user对象可能在事务结束后脱离了EF上下文,确保登录时使用的是有效实体 - 如果
SignInAsync不需要查询数据库(比如直接使用传入的user对象),也可以直接在事务完成后调用,但重新查询更稳妥
内容的提问来源于stack exchange,提问作者Ayobamilaye
相关产品推荐
相关产品推荐

