Entity Framework插入UserDetail时自动新增UserRole行的问题求助
问题分析与解决办法
嘿,我懂你现在遇到的困扰:每次往UserDetail表插数据时,UserRole表总会自动新增一行,明明你已经在控制器里硬编码指定了角色ID对吧?这其实是Entity Framework的上下文跟踪机制在搞鬼,我来给你拆解下原因,再提供几个可行的解决办法。
问题根源
你看这段代码里的关键问题:
// 新建了一个独立的上下文实例 UserContext ctx = new UserContext(); UserDetail user = new UserDetail { // 从这个独立上下文里取出了UserRole对象 UserRole = ctx.UserRole.Where(id => id.RoleID == 2).FirstOrDefault(), // 其他属性赋值... }; // 但你用_unitofwork的上下文来添加UserDetail _unitofwork.userDetail.Add(user);
问题就出在两个不同的上下文实例上:你从ctx这个上下文拿到的UserRole,并没有被_unitofwork的上下文跟踪。EF会默认认为这个UserRole是个全新的实体,所以在保存UserDetail的时候,会自动把这个角色也插入到数据库里。
解决方案
方案1:用同一个上下文查询和添加(最推荐)
既然_unitofwork已经封装了上下文,直接用它来查询角色就好,这样整个操作都在同一个上下文里,EF能正确识别角色是已存在的:
[HttpPost] public HttpResponseMessage Register(UserDetail usrInfo) { // 直接通过_unitofwork获取角色 var targetRole = _unitofwork.UserRole.FirstOrDefault(r => r.RoleID == 2); if(targetRole == null) { return Request.CreateResponse(HttpStatusCode.NotFound, "指定的角色不存在"); } UserDetail user = new UserDetail { UserRole = targetRole, FirstName = usrInfo.FirstName, LastName = usrInfo.LastName, UserName = usrInfo.UserName, Password = usrInfo.Password, Email = usrInfo.Email, }; _unitofwork.userDetail.Add(user); if (_unitofwork.Completed() > 0) return Request.CreateResponse(HttpStatusCode.OK, "用户创建成功"); else return Request.CreateResponse(HttpStatusCode.BadRequest, "用户创建失败"); }
方案2:手动把角色附加到目标上下文
如果因为某些原因必须用不同的上下文,那你可以把查询到的角色附加到_unitofwork的上下文里,并且标记它为“已存在”:
[HttpPost] public HttpResponseMessage Register(UserDetail usrInfo) { UserContext ctx = new UserContext(); var targetRole = ctx.UserRole.FirstOrDefault(r => r.RoleID == 2); if(targetRole == null) { return Request.CreateResponse(HttpStatusCode.NotFound, "指定的角色不存在"); } UserDetail user = new UserDetail { UserRole = targetRole, FirstName = usrInfo.FirstName, LastName = usrInfo.LastName, UserName = usrInfo.UserName, Password = usrInfo.Password, Email = usrInfo.Email, }; // 把角色附加到_unitofwork的上下文,并标记为未修改(已存在) _unitofwork.UserRole.Attach(targetRole); _unitofwork.Entry(targetRole).State = EntityState.Unchanged; _unitofwork.userDetail.Add(user); if (_unitofwork.Completed() > 0) return Request.CreateResponse(HttpStatusCode.OK, "用户创建成功"); else return Request.CreateResponse(HttpStatusCode.BadRequest, "用户创建失败"); }
方案3:直接用外键关联(最简洁)
其实你根本不需要查询整个UserRole对象,给UserDetail加一个外键属性,直接设置角色ID就行,EF会自动处理关联:
第一步,修改UserDetail实体,添加外键:
public class UserDetail { [Key] public int UserID { get; set; } // 其他原有属性... // 添加RoleID外键 public int RoleID { get; set; } // 配置导航属性和外键的关联 [ForeignKey(nameof(RoleID))] public UserRole UserRole { get; set; } }
然后修改控制器代码,直接赋值RoleID:
[HttpPost] public HttpResponseMessage Register(UserDetail usrInfo) { UserDetail user = new UserDetail { RoleID = 2, // 直接设置硬编码的角色ID FirstName = usrInfo.FirstName, LastName = usrInfo.LastName, UserName = usrInfo.UserName, Password = usrInfo.Password, Email = usrInfo.Email, }; _unitofwork.userDetail.Add(user); if (_unitofwork.Completed() > 0) return Request.CreateResponse(HttpStatusCode.OK, "用户创建成功"); else return Request.CreateResponse(HttpStatusCode.BadRequest, "用户创建失败"); }
这个方案最省心,既不用查询角色,也不会有上下文跟踪的问题,完全避开了导致新增角色的根源。
内容的提问来源于stack exchange,提问作者Aniruddha
相关产品推荐
相关产品推荐

