附加类型的实体失败,因为相同类型的另一个实体已具有相同的主键值

错误消息:附加类型的实体失败,因为同一类型的另一个实体已具有相同的主键值。

问题:如何以类似的方式附加实体,如下面的代码中的AttachActivity方法所示?

我必须假设上面的错误消息的“另一个实体”部分是指存在于内存但超出范围(??)的对象。 我注意到这一点,因为我试图附加的实体类型的DBSet的Local属性返回零。

我有理由相信实体在上下文中不存在,因为我逐步完成代码并在创建时观察上下文。 在创建dbcontext之后,实体将立即添加到几行中。

我正在测试这里指定的附加实体: 找出实体是否附加到dbContext的最合理方法是什么?

当在visual studio的locals窗口中查看本地时,我看到没有类型为Activity的实体(无论ID),除了我想要附加的实体。

代码按以下顺序执行:Try – > ModifyProject – > AttachActivity

代码在注释行的AttachActivity中失败。

请注意调试注释之间的代码,如果已将任何实体添加到上下文中,则将抛出该代码。

private string AttachActivity(Activity activity) { string errorMsg = ValidateActivity(activity); // has no code yet. No. It does not query db. if(String.IsNullOrEmpty(errorMsg)) { // debug var state = db.Entry(activity).State; // Detached int activityCount = db.Activities.Local.Count; int projectCount = db.Activities.Local.Count; if (activityCount > 0 || projectCount > 0) throw new Exception("objects exist in dbcontext"); // end debug if (activity.ID == 0) db.Activities.Add(activity); else { db.Activities.Attach(activity); // throws here db.Entry(activity).State = System.Data.Entity.EntityState.Modified; } } return errorMsg; } public int ModifyProject(Presentation.PresProject presProject, out int id, out string errorMsg) { // snip foreach (PresActivity presActivity in presProject.Activities) { Activity a = presActivity.ToActivity(); // returns new Activity object errorMsg = ValidateActivity(a); // has no code yet. No. It does not query db. if (String.IsNullOrEmpty(errorMsg)) { a.Project = project; project.Activities.Add(a); AttachActivity(a); } else break; } if (string.IsNullOrEmpty(errorMsg)) { if (project.ID == 0) db.Projects.Add(project); else db.AttachAsModfied(project); saveCount = db.SaveChanges(); id = project.ID; } return saveCount; } 

这是新闻dbContext的类:

 public void Try(Action work) { using(IServices client = GetClient()) // dbContext is newd up here { try { work(client); // ModifyProject is called here HangUp(client, false); } catch (CommunicationException e) { HangUp(client, true); } catch (TimeoutException e) { HangUp(client, true); } catch (Exception e) { HangUp(client, true); throw; } } 

我不是问:我如何使用AsNoTracking有什么区别.AsNoTracking()会做什么?

避免接收此错误的一种解决方案是使用Find方法。 在附加实体之前,查询所需实体的DbContext ,如果实体存在于内存中,则获得本地实体,否则将从数据库中检索实体。

 private void AttachActivity(Activity activity) { var activityInDb = db.Activities.Find(activity.Id); // Activity does not exist in database and it's new one if(activityInDb == null) { db.Activities.Add(activity); return; } // Activity already exist in database and modify it db.Entry(activityInDb).CurrentValues.SetValues(activity); db.Entry(activityInDb ).State = EntityState.Modified; } 

附加类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。 如果图中的任何实体具有冲突的键值,则在使用Attach方法或将实体的状态设置为UnchangedModified时会发生这种情况。 这可能是因为某些实体是新的并且尚未收到数据库生成的键值。 在这种情况下使用Add

解决方案就是这样

如果你不得不使用GetAll()

 public virtual IEnumerable GetAll() { return dbSet.ToList(); } 

改成

 public virtual IEnumerable GetAll() { return dbSet.AsNoTracking().ToList(); } 

我通过更改下面的Update方法解决了这个错误。

如果您使用通用存储库和实体

 _dbContext.Set().AddOrUpdate(entityToBeUpdatedWithId); 

或者是普通(非通用)存储库和实体

 _dbContext.Set().AddOrUpdate(entityToBeUpdatedWithId); 

如果您使用AddOrUpdate()方法,请确保添加了“ System.Data.Entity.Migrations ”命名空间。