10

Error message: Attaching an entity of type failed because another entity of the same type already has the same primary key value.

Question: How do I attached an entity in a similar fashion as demonstrated in the AttachActivity method in the code below?

I have to assume the "another entity" part of the error message above refers to an object that exists in memory but is out of scope (??). I note this because the Local property of the DBSet for the entity type I am trying to attach returns zero.

I am reasonably confident the entities do not exist in the context because I step through the code and watch the context as it is created. The entities are added in the few lines immediately following creation of the dbcontext.

Am testing for attached entities as specified here:what is the most reasonable way to find out if entity is attached to dbContext or not?

When looking at locals in the locals window of visual studio I see no entities of type Activity (regardless of ID) except the one I am trying to attach.

The code executes in this order: Try -> ModifyProject -> AttachActivity

Code fails in the AttachActivity at the commented line.

Note the code between the debug comments which will throw if any entities have been added to the context.

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;
    }

This is the class that news up the 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;
            }
        }

I am not asking: How do I use AsNoTracking What difference does .AsNoTracking() make?

Community
  • 1
  • 1
  • Why does Entity Framework Reinsert Existing Objects into My Database? msdn.microsoft.com/en-us/magazine/dn166926.aspx – Colin Nov 12 '14 at 11:19
  • @Colin - good catch - and I did think of that. In my project the Project class has a property which is a List. However, in the debug code in AddActivity I am checking for any project objects added to the context and there are none. –  Nov 12 '14 at 15:05
  • Please have a look at my answer on [ASP.NET MVC - Attaching an entity of type 'MODELNAME' failed because another entity of the same type already has the same primary key value](http://stackoverflow.com/questions/23201907/asp-net-mvc-attaching-an-entity-of-type-modelname-failed-because-another-ent/39557606#39557606). – Murat Yıldız Sep 18 '16 at 12:25
  • Clear all State dbContextGlobalERP.ChangeTracker.Entries().Where(e => e.Entity != null).ToList().ForEach(e => e.State = EntityState.Detached); – xxxsenatorxxx Jun 15 '20 at 14:52

3 Answers3

32

One solution to avoid receiving this error is using Find method. before attaching entity, query DbContext for desired entity, if entity exists in memory you get local entity otherwise entity will be retrieved from database.

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;
}
Mohsen Esmailpour
  • 11,224
  • 3
  • 45
  • 66
  • I am attaching the object with a state of Modified which should generate an update statement for the db. I accidently clicked the downvote button so I upvoted to cancel it. The answer shows one upvote it should be zero. –  Nov 12 '14 at 14:54
6

Attaching an entity of type failed because another entity of the same type already has the same primary key value. This can happen when using the Attach method or setting the state of an entity to Unchanged or Modified if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the Add.

The solution is that

if you had to use GetAll()

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

Change To

public virtual IEnumerable<T> GetAll()
{
    return dbSet.AsNoTracking().ToList();
}
Mohsen Esmailpour
  • 11,224
  • 3
  • 45
  • 66
Jaykishan
  • 81
  • 1
  • 4
4

I resolved this error by changing Update method like below.

if you are using generic repository and Entity

_dbContext.Set<T>().AddOrUpdate(entityToBeUpdatedWithId);

or normal(non-generic) repository and entity , then

_dbContext.Set<TaskEntity>().AddOrUpdate(entityToBeUpdatedWithId);

If you use AddOrUpdate() method, please make sure you have added System.Data.Entity.Migrations namespace.

Mohsen Esmailpour
  • 11,224
  • 3
  • 45
  • 66
dush88c
  • 1,918
  • 1
  • 27
  • 34
  • 1
    Take care with the AddOrUpdate Method: https://stackoverflow.com/questions/26879968/attaching-an-entity-of-type-failed-because-another-entity-of-the-same-type-alrea – Colin Apr 10 '18 at 13:36