4

I use the Unity of Work and Generic Repository of CodeCamper.

to update an entity, the generic repo has:

    public virtual void Update(T entity)
    {
        DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
        if (dbEntityEntry.State == EntityState.Detached)
        {
            DbSet.Attach(entity);
        }  
        dbEntityEntry.State = EntityState.Modified;
    }

the web api method:

    public HttpResponseMessage Put(MyEditModel editModel)
    {
        var model = editModel.MapToMyEntity();
        _myManager.Update(model);
        return new HttpResponseMessage(HttpStatusCode.NoContent);
    }

The Update method:

    public void Update(MyEntity model)
    {
        Uow.MyEntities.Update(model);
        Uow.Commit();
    }

In the Unityof Work:

IRepository<MyEntity> MyEntities { get; }

When updating an entity I get the following error:

Additional information: Attaching an entity of type 'X' 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' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

  1. The update works fine, when it is the first method you call of the repository. (I created an entity with an id already in the DB and called the Update.)

  2. The update doesn't work when you do a get of the entity before you update it. (For example, I get an entity X, convert it to a DTO, then change some values in the UI, then call a web api that creates an entity X with the new values and call the Update of the repository.)

Any ideas to avoid this? When you have a CRUD app, you always call the get before the update.

Filip
  • 1,098
  • 5
  • 17
  • 34
  • I think the lookup made by `Entry` is based on .Net references hence the "issue". Instead you should try to first find the entity in the `Local` collection. – Pragmateek Jul 12 '14 at 17:44
  • I read something like "detach the original in the context and then attach the new/updated entity", but I don't like that solution. – Filip Jul 12 '14 at 18:23
  • As soon as you do your `get`, the entity is loaded into your context. Since your update fails after the `get`, you must be trying to attach a duplicate entity somehow. Can you try detaching the entity after the `get` once you've mapped it to a DTO? – Mister Epic Jul 12 '14 at 18:56
  • @Filip Can you post the web api code that handles this: "then call a web api that creates an entity X with the new values and call the Update of the repository"? – Anthony Chu Jul 13 '14 at 06:42
  • Is the primary key in your database set to auto-increment? – rhughes Jul 13 '14 at 08:12
  • @rhughes I want to update, not add an item, so the auto increment isn't the cause? – Filip Jul 13 '14 at 09:17
  • @ChrisHardie Detaching is not what I want, because when you do a 'get all' (for a grid with knockout) and then update the entity, you must detach all the entities after the get all. – Filip Jul 13 '14 at 10:42
  • @Filip Then you'll somehow need to architect things so that you can update the entity that already resides in your context. – Mister Epic Jul 13 '14 at 12:11
  • You might 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:28

2 Answers2

2

I'm using my own attach method:

public void Attach<E>(ref E entity)
{
    if (entity == null)
    {
        return;
    }

    try
    {
        ObjectStateEntry entry;
        bool attach = false;
        if (ObjectStateManager.TryGetObjectStateEntry(CreateEntityKey(entitySetName, entity), out entry))
        {
            attach = entry.State == EntityState.Detached;

            E existingEntityInCache = (E)entry.Entity;
            if (!existingEntityInCache.Equals(entity))
            {
                existingEntityInCache.SetAllPropertiesFromEntity(entity);
            }
            entity = existingEntityInCache;
        }
        else
        {
            attach = true;
        }
        if (attach)
            objectContext.AttachTo(entitySetName, entity);
    }
    catch (Exception ex)
    {
        throw new Exception("...");
    }
}
Cyrus
  • 2,261
  • 2
  • 22
  • 37
0

I had the same issue. The problem was in mixed contexts. When you read entity from DB in context1. Then if you can update this entity with contex2 (other instance of the same context with own entity cache). This may throw an exception.

Pls check for references too:

by context1: read entity1 with referenced entity2 from DB

by context2: read entity2 from DB. Then update entity1 (with referenced entity2 from context1).

When you try attach entity1 with referenced entity2 to context2, this throw exception because entity2 already exists in context2.

The solution is use only one context for this operation.

Cyrus
  • 2,261
  • 2
  • 22
  • 37