我将在下面提到这个问题:我知道互联网上有一百万个帖子关于旧的“具有相同密钥的对象已存在于ObjectStateManager”问题。我认为我的情况有点复杂。
我有一个UnitOfWork类,它创建一个DbContext并将其传递给任何被调用的存储库。我正在使用的模式紧跟ASP.NET站点上的工作单元教程。与本教程不同,我的存储库采用业务实体,将它们映射到数据实体,并执行一些CRUD操作。我的业务逻辑仅适用于业务实体。以下是我在示例Business Manager类中尝试做的事情:
_unitOfWork.Repository.Add(entity);
_unitOfWork.Save(); // context.SaveChanges() under the hood
...Perform some operations on the model...
_unitOfWork.Repository.Update(entity);
_unitOfWork.Save();
以下是来自存储库的示例Update方法:
public virtual void Update(entity)
{
var dataEntity = // map from business entity to data;
_context.Entry(dataEntity).State = EntityState.Modified;
}
在最后一行显然失败了。这就是我的困惑所在:
存储库中的Update方法需要处理两种情况:1)更新当前未被上下文跟踪的实体,以及2)更新当前由上下文跟踪的实体。第二部分是我正在努力的事情。
感谢任何帮助或见解。
谢谢!
答案 0 :(得分:1)
这意味着已经有一个对象附加到上下文,其中的键与新dataEntity
相同。现有对象和新实体都表示数据库中的相同条目,但它们是两个不同的对象。
这可能表示_context
的使用寿命太长,但很难从代码中判断出来。可以肯定的是,上下文以前用于从数据库中获取实体,该实体随后由var dataEntity = ...
复制。
你可能不得不缩短上下文的生命周期,我无法分辨。如果您认为没问题,您可能需要使用Local
集合来检查实体是否已经存在。这将保存Find
可能仍会进行的数据库往返。
答案 1 :(得分:0)
我找到了一种似乎有效的混合解决方案:
public virtual void Update(TB entity)
{
var dataEntity = Mapper.Map<TB, TD>(entity);
var pkey = _dbSet.Create().GetType().GetProperty("Id").GetValue(dataEntity);
var entry = _context.Entry(dataEntity);
if (entry.State == EntityState.Detached)
{
var attachedEntity = _dbSet.Find(pkey);
if (attachedEntity != null)
{
var attachedEntry = _context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(dataEntity);
}
else
{
entry.State = EntityState.Modified;
}
}
else
{
entry.State = EntityState.Modified;
}
}