错误消息:附加类型的实体失败,因为同一类型的另一个实体已具有相同的主键值。
问题:如何以与下面代码中AttachActivity方法中演示的方式类似的方式附加实体?
我必须假设另一个实体"上面的错误消息的一部分是指存在于内存但超出范围(??)的对象。我注意到这一点,因为我尝试附加的实体类型的DBSet的Local属性返回零。
我有理由相信实体在上下文中不存在,因为我逐步完成代码并在创建时观察上下文。在创建dbcontext之后,实体将立即添加到几行中。
正在测试此处指定的附加实体:what is the most reasonable way to find out if entity is attached to dbContext or not?
当在visual studio的locals窗口中查看本地时,我看不到Activity类型的实体(无论ID如何),除了我想要附加的实体。
代码按此顺序执行:尝试 - > 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<IServices> 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 What difference does .AsNoTracking() make?
答案 0 :(得分:23)
避免收到此错误的一种解决方法是使用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;
}
答案 1 :(得分:6)
附加类型的实体失败,因为同一类型的另一个实体已具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用Attach
方法或将实体的状态设置为Unchanged
或Modified
时,可能会发生这种情况。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,请使用Add
。
解决方案就是
如果你必须使用GetAll()
public virtual IEnumerable<T> GetAll()
{
return dbSet.ToList();
}
更改为
public virtual IEnumerable<T> GetAll()
{
return dbSet.AsNoTracking().ToList();
}
答案 2 :(得分:3)
我通过更改下面的Update方法解决了这个错误。
如果您使用的是通用存储库和实体
_dbContext.Set<T>().AddOrUpdate(entityToBeUpdatedWithId);
或普通(非通用)存储库和实体,然后
_dbContext.Set<TaskEntity>().AddOrUpdate(entityToBeUpdatedWithId);
如果您使用AddOrUpdate()方法,请确保已添加 &#34;的 System.Data.Entity.Migrations 强>&#34;命名空间。