如何让else分支触发DbUpdateConcurrencyException捕获逻辑或获取异常变量?
解决ASP.NET Identity更新用户时的并发冲突与数据库值获取问题
首先咱们得先理清核心问题:UserManager.UpdateAsync方法在遇到并发冲突时,默认不会抛出DbUpdateConcurrencyException——它会把错误信息封装到返回的IdentityResult里,所以你的catch块根本不会被触发,这就是为什么你在else分支里没法进入catch的原因。
针对你的需求,有两种可行的处理方式:
方式一:手动触发并发异常(不推荐)
如果你一定要让代码进入catch块,可以绕开UserManager,直接用DbContext执行更新操作,这样EF Core会在检测到并发冲突时自动抛出DbUpdateConcurrencyException:
try { user.UserName = editedUser.Username; user.Email = editedUser.Email; user.RowVersion = editedUser.RowVersion; // 直接使用DbContext而非UserManager _context.Update(user); await _context.SaveChangesAsync(); return RedirectToAction("ListOfUsers"); } catch (DbUpdateConcurrencyException ex) { // 这里就能正常触发你原来的并发处理逻辑 var exceptionEntry = ex.Entries.Single(); var clientValues = (ApplicationUser)exceptionEntry.Entity; var databaseEntry = exceptionEntry.GetDatabaseValues(); // ... 你的原有处理代码 }
不过这种方式会跳过UserManager自带的验证、日志等核心逻辑,所以一般不建议这么做。
方式二:在else分支直接查询数据库当前值(推荐,优化你的方案)
更合理的做法是放弃依赖catch块,直接在UpdateAsync失败的分支里主动查询数据库的最新用户数据,对比差异。你已经找到了核心思路,这里可以优化细节让逻辑更严谨:
try { user.UserName = editedUser.Username; user.Email = editedUser.Email; user.RowVersion = editedUser.RowVersion; var result = await userManager.UpdateAsync(user); if (result.Succeeded) { return RedirectToAction("ListOfUsers"); } else { // 先判断是否是并发冲突导致的更新失败 if (result.Errors.Any(e => e.Code == "ConcurrencyFailure")) { // 用AsNoTracking获取数据库最新数据,避免上下文跟踪干扰 var dbUser = await userManager.Users.AsNoTracking() .SingleOrDefaultAsync(x => x.Id == user.Id); if (dbUser == null) { ModelState.AddModelError(string.Empty, $"User {editedUser.ID} was deleted before you applied the changes."); } else { // 对比客户端提交值与数据库最新值 if (dbUser.UserName != user.UserName) { ModelState.AddModelError("Username", $"Current username in Database: {dbUser.UserName}"); } if (dbUser.Email != user.Email) { ModelState.AddModelError("Email", $"Current email in Database: {dbUser.Email}"); } ModelState.AddModelError(string.Empty, "The user you were trying to edit was modified by someone else. Editing operation was canceled and the current values in the database are now shown below. If you still want to edit this user, click Save again, otherwise click Undo"); // 更新RowVersion为最新值,方便用户再次提交时验证 user.RowVersion = dbUser.RowVersion; ModelState.Remove("RowVersion"); } } else { // 处理其他更新失败场景,比如用户名重复、邮箱格式错误等 foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } } } catch (Exception ex) { // 处理意外异常 ModelState.AddModelError(string.Empty, "An unexpected error occurred while updating the user."); } // 返回编辑页面展示错误信息与最新数据 return View(editedUser);
为什么推荐这种方式?
- 保留了
UserManager的所有内置逻辑(用户验证、规则检查等) - 能精准区分并发冲突和其他更新错误
- 逻辑更清晰,不需要依赖异常捕获来处理业务场景
内容的提问来源于stack exchange,提问作者DM-98
相关产品推荐
相关产品推荐

