“An object with the same key already exists in the

2019-02-01 23:40发布

问题:

I followed some examples(including such books as "Pro ASP.NET MVC 3" and "Professional ASP.NET MVC 3") to create simple ASP.NET MVC 3 apps using EF 4.1 (since I'm new to these technologies).

I'm using the following repository(single instance of it is used by all action methods of the controller) to access the DB:

public class ProductRepository : IProductRepository
    {
        private readonly EFDbContext _context = new EFDbContext();

        #region Implementation of IProductRepository       

       ....

        public void SaveProduct(Product product)
         {           
            if (product.ProductId == 0)
            {
                _context.Products.Add(product);
            }
            else
            {
                _context.Entry(product).State = EntityState.Modified;

            }

            _context.SaveChanges();
        }

....
}

This repository performs updating as it was shown in the examples I used.

Product class:

public class Product
    {       
        public int ProductId { get; set; }       
        public string Name { get; set; }      
        public string Description { get; set; }     
        public decimal Price { get; set; }
        public string Category { get; set; }
}

In case of updating the product, I'm getting the exception "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key"

I know that the similar questions have been already discussed here but my question is a bit different:

Why this code which was taken from examples is not working (though it looks pretty simple and straightforward)? What wrong might I have done or missed something.

回答1:

After searching for hours for a solution, I have found one that seems suitable after doing enough reading.

The fix is here:

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

Basically, fetch the record from the Context and call:

var currentProduct = _context.Products.Find(product.ProductId);    
_context.Entry(currentProduct).CurrentValues.SetValues(product);

This seems like a bad idea and something I've always hated about EF in my previous workings, but cccording to Ladislav Mrnka (who apparnently answers every EF related question on Stackoverflow) in this post:

Entity Framework and Connection Pooling

EF will store a request for an entity internally, so ideally, it will already be there and it won't be making an additional call back to the database.

The root cause of the problem seems to be that once a product is fetched from the Context, the context is keeping track of it and that's what is causing all the trouble. So merging your changes back in is the only way.

Hope that helps.



回答2:

It looks like you're not updating product.ProductId when the item is saved for the first time. This means that when you come back to save the item again it's adding it to the context again, hence the error.

As the Id will be added by database (I'm assuming it's the autogenerated Id) then you'll need to read your product data back onto the client.



回答3:

From a Generics standpoint, here's how I have resolve the same problem very recently:

    public TEntity Update(TEntity model, bool persist)
    {
        if (model == null)
        {
            throw new ArgumentException("Cannot update a null entity.");
        }

        var updateModel = Get(model.Id);

        if (updateModel == null)
        {
            return model;   
        }

        this.context.Entry<TEntity>(updateModel).CurrentValues.SetValues(model);

        this.Save(persist);

        return model;
    }