"The ObjectStateManager cannot monitor multiple objects with the same key," says Entity Framework Code-First.

asp.net-mvc-3 code-first dbcontext entity-framework repository

Question

Code-first Entity Framework in MVC3 is giving me problems. I'm running across this problem:

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

This has been discussed on SO many occasions, but none of the answers that have been offered have worked for me.

Here is an example of code:

FestORM.SaleMethod method = new FestORM.SaleMethod
{
    Id = 2,
    Name = "Test Sale Method"
};
FestContext context = new FestContext();

//everything works without this line:
string thisQueryWillMessThingsUp = 
    context.SaleMethods.Where(m => m.Id == 2).Single().Name;

context.Entry(method).State = System.Data.EntityState.Modified;
 context.SaveChanges();

EDITED to make clear: I'm trying to edit a database item that already exists.

Without the query mentioned in the code, everything functions well. I am unable to just utilize a different context for the first query operation in my application since my controller creates the context and passes it to multiple repositories that the controller uses. I've also attempted to stop the entity from being monitored by the ObjectStateManager, but I'm having no luck. I'm attempting to come up with a solution that will work in both scenarios: sometimes, I'll be modifying an object that the ObjectStateManager is tracking, and occasionally, it could not have been monitored at all.

For the record, the functionalities of my actual repository are shown in the code above:

public void Update(T entity)
{
    //works ONLY when entity is not tracked by ObjectStateManager
    _context.Entry(entity).State = System.Data.EntityState.Modified; 
}

public void SaveChanges()
{
    _context.SaveChanges();
}

Any thoughts? I've fought against this for far too long.

1
5
3/8/2012 1:23:46 AM

Accepted Answer

The issue is that this question

string thisQueryWillMessThingsUp =  
    context.SaleMethods.Where(m => m.Id == 2).Single().Name; 

introduces one SaleMethod entity object into the context before this code is executed.

context.Entry(method).State = System.Data.EntityState.Modified;

gives context to another occurrence. Since the main key is the same for both instances, EF believes you are attempting to connect two distinct entities with the same key to the context. It is unaware that they are meant to be one and the same thing.

You may follow these steps if, for any reason, you just need to query for the name and don't want to actually introduce the whole object into the context:

string thisQueryWillMessThingsUp =           
    context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name; 

The easiest way to do is to just use: instead of running the query, if you are trying to update an existing object and you have values for all of its mapped properties.

context.Entry(method).State = System.Data.EntityState.Modified;

It's fine to query for the entity and set properties on it before using SaveChanges if you don't want to update all properties, potentially because you don't have values for all properties. Depending on your precise needs, there are a number of methods to do this. Using the Property method is one option, like in the following example:

var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName;
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue;
context.SaveChanges();

Additional details in these blog postings that could be useful include:

http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx

http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx

12
3/8/2012 2:57:31 AM

Popular Answer

The simple explanation is that before calling, you aren't really storing the method object to the database.

//everything works without this line:
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name;

I do believe that you may have omitted a little portion of the code, however. What if your entities were to inherit from an abstract class, such as

public abstract class BaseClass
{
     public int Id { get; set; }
}

Update your Repository after that.

public class Repository<T> where T : BaseClass
{
 .....
    public void Update(T entity)
    {        
        _context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified; 
    }
}

Additionally, you may want to let the database create your SaleMethod's ID rather than setting it. Problem might also arise if you attempt to create a second SaleMethod object with Id 2 after the first one in the database has Id of 2. The attempt to add a second SaleMethod object to the ObjectStateManager with ID 2 is what caused the problem you saw.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow