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

Sam*_*Sam 8 .net c# entity-framework

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

问题:如何以类似的方式附加实体,如下面的代码中的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;
    }
Run Code Online (Sandbox Code Playgroud)

这是新闻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;
            }
        }
Run Code Online (Sandbox Code Playgroud)

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

Moh*_*our 25

避免接收此错误的一种解决方案是使用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;
}
Run Code Online (Sandbox Code Playgroud)


小智 6

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

解决方案就是这样

如果你不得不使用GetAll()

public virtual IEnumerable<T> GetAll()
{
    return dbSet.ToList();
}
Run Code Online (Sandbox Code Playgroud)

改成

public virtual IEnumerable<T> GetAll()
{
    return dbSet.AsNoTracking().ToList();
}
Run Code Online (Sandbox Code Playgroud)