EF Core使用Identity时出现FOREIGN KEY约束冲突如何修复
问题描述
我尝试使用Microsoft.AspNetCore.Identity生成id用于填充userData.AspNetUserID属性时,控制台抛出如下错误:
System.Data.SqlClient.SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_AspNetUserRoles_AspNetUsers_UserId". The conflict occurred in database "JET", table "dbo.AspNetUsers", column 'Id'.
相关实现代码如下:
public async Task<IActionResult> CopyUserRole(int id) { var result = _context.UserData.Where(m => m.UserDataID == id).FirstOrDefault(); var userData = new UserData(); var email = result.Email.Insert(0, "copy-"); var user = new ApplicationUser { UserName = email, Email = email }; var UserManager = await _userManager.CreateAsync(user, "12345678"); if(!await _roleManager.RoleExistsAsync(result.Role)) { await _roleManager.CreateAsync(new IdentityRole(result.Role)); } var success = await _userManager.AddToRoleAsync(user, result.Role); //userData.UserDataID = 0; userData.AspNetUserID = user.Id; userData.DeviceID = result.DeviceID; userData.PhotoUrl = result.PhotoUrl; userData.Status = result.Status; userData.Role = result.Role; userData.Name = result.Name+" - Copy"; userData.Area = result.Area; userData.FilePhotoURL1 = result.FilePhotoURL1; userData.Email = email; userData.LihatJurnal = result.LihatJurnal; userData.UserDataArea = result.UserDataArea; userData.UserDataMenu = result.UserDataMenu; userData.UserDataGudang = result.UserDataGudang; userData.UserDataPerkiraan = result.UserDataPerkiraan; try { _context.UserData.Add(userData); await _context.SaveChangesAsync(); } catch(Exception Ex) { Console.WriteLine("\n ======================== \n"); Console.WriteLine("\n " + Ex + " \n"); Console.WriteLine("\n ======================== \n"); } return Json(userData); }
错误原因
这个外键冲突的核心原因是代码完全没检查_userManager.CreateAsync的执行结果:
- 如果用户创建失败(比如密码不符合规则、邮箱重复、用户名校验不通过),
user对象不会被持久化到AspNetUsers表中,此时user.Id是默认的空值/临时值,根本不存在于用户表 - 后续直接调用
AddToRoleAsync给不存在的用户绑定角色,插入AspNetUserRoles表时就会触发外键约束报错,因为关联的UserId在用户表里找不到
代码里还有两个隐藏问题:
- 没有判断查询出来的
result是否为null,如果传入的id不存在,后续访问result.Email会直接抛空引用异常 - 角色创建、用户绑定角色的操作结果也没有校验,任意一步失败都会导致后续逻辑出错
修复方案
调整代码逻辑,每一步异步操作都先判断执行结果,失败时直接返回错误,不要继续往下走:
public async Task<IActionResult> CopyUserRole(int id) { // 先判断源数据是否存在 var result = _context.UserData.FirstOrDefault(m => m.UserDataID == id); if (result == null) { return Json(new { success = false, message = "待复制的用户不存在" }); } var userData = new UserData(); var email = result.Email.Insert(0, "copy-"); var user = new ApplicationUser { UserName = email, Email = email }; // 检查用户创建结果 var createUserResult = await _userManager.CreateAsync(user, "12345678"); if (!createUserResult.Succeeded) { return Json(new { success = false, message = "用户创建失败", errors = createUserResult.Errors }); } // 检查角色是否存在,不存在则创建 if(!await _roleManager.RoleExistsAsync(result.Role)) { var createRoleResult = await _roleManager.CreateAsync(new IdentityRole(result.Role)); if (!createRoleResult.Succeeded) { return Json(new { success = false, message = "角色创建失败", errors = createRoleResult.Errors }); } } // 检查用户绑定角色结果 var addToRoleResult = await _userManager.AddToRoleAsync(user, result.Role); if (!addToRoleResult.Succeeded) { return Json(new { success = false, message = "用户角色绑定失败", errors = addToRoleResult.Errors }); } // 所有Identity操作成功后,再给UserData赋值 userData.AspNetUserID = user.Id; userData.DeviceID = result.DeviceID; userData.PhotoUrl = result.PhotoUrl; userData.Status = result.Status; userData.Role = result.Role; userData.Name = result.Name + " - Copy"; userData.Area = result.Area; userData.FilePhotoURL1 = result.FilePhotoURL1; userData.Email = email; userData.LihatJurnal = result.LihatJurnal; userData.UserDataArea = result.UserDataArea; userData.UserDataMenu = result.UserDataMenu; userData.UserDataGudang = result.UserDataGudang; userData.UserDataPerkiraan = result.UserDataPerkiraan; try { _context.UserData.Add(userData); await _context.SaveChangesAsync(); } catch(Exception Ex) { Console.WriteLine("\n ======================== \n"); Console.WriteLine("\n " + Ex + " \n"); Console.WriteLine("\n ======================== \n"); return Json(new { success = false, message = "用户数据保存失败" }); } return Json(new { success = true, data = userData }); }
额外注意:代码里设置的初始密码是12345678,要确认和Startup.cs/Program.cs里配置的Identity密码规则匹配,比如是否要求特殊字符、大小写、数字长度,否则会一直出现用户创建失败的问题。
内容的提问来源于stack exchange,提问作者Rizal Maulana
相关产品推荐
相关产品推荐

