当将实体状态设置为修改时,如果在ObjectStateManager中已经存在具有相同键的对象,则会抛出"在ObjectStateManager中已经存在具有相同键的对象..."异常。

12 浏览
0 Comments

当将实体状态设置为修改时,如果在ObjectStateManager中已经存在具有相同键的对象,则会抛出"在ObjectStateManager中已经存在具有相同键的对象..."异常。

我根据一些例子(包括《Pro ASP.NET MVC 3》和《Professional ASP.NET MVC 3》等书籍)创建了一些简单的ASP.NET MVC 3应用程序,使用了EF 4.1(因为我对这些技术还不熟悉)。

我使用以下存储库(控制器的所有操作方法都使用它的单个实例)来访问数据库:

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();
        }
....
}

这个存储库执行的是我使用的例子中所示的更新操作。

产品类:

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; }
}

在更新产品时,我得到了异常“一个具有相同键的对象已经存在于ObjectStateManager中。ObjectStateManager无法跟踪具有相同键的多个对象”。

我知道类似的问题在这里已经讨论过,但我的问题有点不同:

为什么这段代码从例子中拿来的不起作用(尽管看起来非常简单和直接)?我可能做错了什么或者遗漏了什么。

0
0 Comments

出现这个问题的原因是在设置实体状态为"修改"时,ObjectStateManager中已经存在具有相同键的对象。解决方法是在更新实体之前,先检查是否已存在相同键的对象,并根据需要进行处理。

最近我遇到了同样的问题,并通过以下泛型方法解决了这个问题:

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(updateModel).CurrentValues.SetValues(model);
    this.Save(persist);
    return model;
}

在这个方法中,首先检查传入的实体是否为空,如果为空则抛出异常。然后通过传入的实体的Id从数据库中获取对应的实体。如果获取到的实体为空,则直接返回传入的实体,表示不需要更新。如果获取到了对应的实体,则使用ObjectStateManager的CurrentValues属性将传入的实体的值更新到获取到的实体中。最后调用Save方法将更改保存到数据库中,并返回传入的实体。

通过这个方法,我们可以避免出现"An object with the same key already exists in the ObjectStateManager..."异常,并成功更新实体的状态。

0
0 Comments

出现这个问题的原因是在保存项目时没有更新product.ProductId。这意味着当您再次保存项目时,它会再次添加到上下文中,导致出现错误。

由于Id将由数据库添加(假设它是自动生成的Id),所以您需要将产品数据重新读取到客户端上。

解决方法是在保存项目时更新product.ProductId。这样,当您再次保存项目时,上下文就不会将它再次添加到上下文中,从而避免出现错误。

以下是一个示例代码,展示了如何更新product.ProductId

// 保存项目时更新product.ProductId
if (product.ProductId == 0)
{
    // 保存项目前,先读取产品数据
    var existingProduct = dbContext.Products.Find(product.Id);
    if (existingProduct != null)
    {
        product.ProductId = existingProduct.ProductId;
    }
}
dbContext.Entry(product).State = EntityState.Modified;
dbContext.SaveChanges();

通过以上的代码,您可以确保在保存项目时更新product.ProductId,从而避免出现"An object with the same key already exists in the ObjectStateManager..."异常。

0
0 Comments

"An object with the same key already exists in the ObjectStateManager..."异常是在设置实体状态为修改时抛出的。解决方法是从上下文中获取记录,并调用`_context.Entry(currentProduct).CurrentValues.SetValues(product);`来更新记录。

根据Ladislav Mrnka在Stackoverflow的回答,这似乎是一个不好的解决办法,他在这篇帖子中表示,EF会在内部存储对实体的请求,因此理想情况下,实体已经存在且不需要额外的数据库调用。

问题的根本原因似乎是一旦从上下文中获取了产品,上下文就会跟踪该产品,并且这就是导致所有问题的原因。因此,将更改合并回去是唯一的解决办法。

为了避免这种跟踪,可以使用`AsNoTracking()`方法。

参考链接:

- [An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key](https://stackoverflow.com/questions/5672255)

- [Entity Framework and Connection Pooling](https://stackoverflow.com/questions/3653009/3653392#3653392)

- [AsNoTracking()](http://msdn.microsoft.com/en-us/library/gg679352%28v=vs.103%29.aspx)

0