I'm using Entity Framework 6 (using generated models and DbContext) for my project. The architecture I'm using in my program is that the data access layer is abstracted from the application. This means my entities will mostly stay in detached state.
It seems that reattaching any previously connected entities seems impossible (or I'm not doing it properly).
I've tested a case in isolation and was able to narrow the problem down. Here are the steps.
Get the entity from the context and dispose the context. This detaches the entity.
public async Task<ArchiveEntry> GetEntry(string api, string id) { // Omitted some code for simplicity ArchiveEntry entry; using (var db = new TwinTailDb()) { entry = await db.ArchiveEntries.Where(a => a.Id == id).SingleOrDefaultAsync(); } return entry; }
Save the entity regardless if it was changed or not.
public async Task Save() { // Omitted some code for simplicity using (var db = new TwinTailDb()) { db.ArchiveEntries.AddOrUpdate(a => a.Id, this); await db.SaveChangesAsync(); } }
Finally, reattach the detached entity.
public async Task RevertChanges() { using (var db = new TwinTailDb()) { if (db.Entry(this).State == EntityState.Detached && Id != 0) { db.ArchiveEntries.Attach(this); //await db.Entry(this).ReloadAsync(); // Commented since this is irrelevant } } }
Then I run this function to use the code above.
public async Task Test() { ArchiveEntry entry = await ArchiveService.GetEntry(null, "7"); await entry.Save(); await entry.RevertChanges(); }
Then it throws this error:
Attaching an entity of type 'TwinTail.Entities.ArchiveEntry' 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.
Here's an important point. If I skip step 2, it doesn't throw an exception.
My speculation is that the entity was modified and saved in a different context. If step 2 was skipped, the entity remains unchanged so reattaching it doesn't pose a problem (just guessing). However, this entity is already in a detached state so this should be irrelevant.
Another point, the ChangeTracker
doesn't contain anything during these steps. Also, if I perform any context operation on the detached entity, it throws an exception saying that it should be attached first. I've also noticed that the internal _entitywrapper still has a reference to the old context.
So, finally, here's the question. How do I properly reattach an entity and why does this exception happen.
I've asked something similar in a different question (How to revert changes on detached entities) but felt that I need to post a new one since this is more general.