Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key

Using EF5 with a generic Repository Pattern and ninject for dependency injenction and running into an issue when trying to update an entity to the database utilizing stored procs with my edmx.

my update in DbContextRepository.cs is:

public override void Update(T entity) {     if (entity == null)         throw new ArgumentException("Cannot add a null entity.");      var entry = _context.Entry<T>(entity);      if (entry.State == EntityState.Detached)     {         _context.Set<T>().Attach(entity);         entry.State = EntityState.Modified;     } } 

From my AddressService.cs which goes back to my repository I have:

 public int Save(vw_address address) {     if (address.address_pk == 0)     {         _repo.Insert(address);     }     else     {         _repo.Update(address);     }      _repo.SaveChanges();      return address.address_pk; } 

When it hits the Attach and EntityState.Modified it pukes with the error:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

I have looked through many of the suggestions in stack and on the Internet and not coming up with anything that resolves it. Any work arounds would be appreciated.

Thanks!

like image 256
Juan Avatar asked Sep 25 '12 14:09

Juan


2 Answers

Edit: Original answer used Find instead of Local.SingleOrDefault. It worked in combination with @Juan's Save method but it could cause unnecessary queries to database and else part was probably never executed (executing the else part would cause exception because Find already queried the database and hadn't found the entity so it could not be updated). Thanks to @BenSwayne for finding the issue.

You must check if an entity with the same key is already tracked by the context and modify that entity instead of attaching the current one:

public override void Update(T entity) where T : IEntity {     if (entity == null) {         throw new ArgumentException("Cannot add a null entity.");     }      var entry = _context.Entry<T>(entity);      if (entry.State == EntityState.Detached) {         var set = _context.Set<T>();         T attachedEntity = set.Local.SingleOrDefault(e => e.Id == entity.Id);  // You need to have access to key          if (attachedEntity != null) {             var attachedEntry = _context.Entry(attachedEntity);             attachedEntry.CurrentValues.SetValues(entity);         } else {             entry.State = EntityState.Modified; // This should attach entity         }     } }   

As you can see the main issue is that SingleOrDefault method needs to know the key to find the entity. You can create simple interface exposing the key (IEntity in my example) and implement it in all your entities you want to process this way.

like image 194
Ladislav Mrnka Avatar answered Oct 09 '22 14:10

Ladislav Mrnka


I didn't want to pollute my auto generated EF classes by adding interfaces, or attributes. so this is really a little bit from some of the above answers (so credit goes to Ladislav Mrnka). This provided a simple solution for me.

I added a func to the update method that found the integer key of the entity.

public void Update(TEntity entity, Func<TEntity, int> getKey) {     if (entity == null) {         throw new ArgumentException("Cannot add a null entity.");     }      var entry = _context.Entry<T>(entity);      if (entry.State == EntityState.Detached) {         var set = _context.Set<T>();         T attachedEntity = set.Find.(getKey(entity));           if (attachedEntity != null) {             var attachedEntry = _context.Entry(attachedEntity);             attachedEntry.CurrentValues.SetValues(entity);         } else {             entry.State = EntityState.Modified; // This should attach entity         }     } }   

Then when you call your code, you can use..

repository.Update(entity, key => key.myId); 
like image 44
Geoff Wells Avatar answered Oct 09 '22 14:10

Geoff Wells