EF 5.0无法更新已经跟踪的实体

时间:2013-03-12 18:11:35

标签: asp.net entity-framework entity-framework-5

我将在下面提到这个问题:我知道互联网上有一百万个帖子关于旧的“具有相同密钥的对象已存在于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;
}

在最后一行显然失败了。这就是我的困惑所在:

  • 实体的状态已分离
  • 当我尝试将State更改为Modified或Unchanged时,它会给我上面的ObjectStateManager异常。
  • 当我尝试从上下文中分离实体时(((IObjectContextAdapter)_context).ObjectContext.Detach(entity);)我得到一个关于实体如何不附加到上下文的异常,因此,它无法分离它。非常令人困惑(我确实缺少一些基本的东西)。
  • 许多其他帖子建议我进行数据库调用,在存储库中更新该实体,然后_unitOfWork.Save()。我不喜欢这种方法。我不需要进行不必要的网络调用来更新实体。

存储库中的Update方法需要处理两种情况:1)更新当前未被上下文跟踪的实体,以及2)更新当前由上下文跟踪的实体。第二部分是我正在努力的事情。

感谢任何帮助或见解。

谢谢!

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