一尘不染

具有相同键的对象已存在于ObjectStateManager中。ObjectStateManager无法使用相同的键跟踪多个对象

c#

尝试将EF5与通用存储库模式结合使用,并使用ninject进行依赖关系注入,并在尝试使用带有edmx的存储过程将实体更新到数据库时遇到问题。

我在DbContextRepository.cs中的更新是:

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

从我的AddressService.cs返回到我的存储库,我有:

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

    _repo.SaveChanges();

    return address.address_pk;
}

当它遇到Attach和EntityState.Modified时,它会吐出错误:

具有相同键的对象已存在于ObjectStateManager中。 ObjectStateManager无法使用相同的键跟踪多个对象。

我浏览了堆栈中和Internet上的许多建议,但没有提出任何解决方案。任何变通办法将不胜感激。

谢谢!


阅读 291

收藏
2020-05-19

共1个答案

一尘不染

编辑
:使用原始答案Find代替Local.SingleOrDefault。它与@Juan的Save方法结合使用,但是可能导致对数据库的不必要查询,并且else部分可能从未执行(执行else部分会导致异常,因为Find已查询数据库并且未找到实体,因此无法更新该实体)
。感谢@BenSwayne找到问题。

您必须检查上下文是否已跟踪具有相同键的实体,并修改该实体而不是附加当前实体:

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

如您所见,主要问题是SingleOrDefault方法需要知道查找实体的键。您可以创建暴露密钥的简单界面(IEntity在我的示例中),并在要处理的所有实体中实现它。

2020-05-19