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

时间:2014-11-12 05:35:15

标签: c# .net entity-framework

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

问题:如何以与下面代码中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?

3 个答案:

答案 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方法或将实体的状态设置为UnchangedModified时,可能会发生这种情况。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,请使用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;命名空间。