附加实体到上下文失败,因为它已经存在。

4
我使用CodeCamper的工作单元和通用存储库进行开发相关的操作。
要更新实体,通用存储库有以下方法:
    public virtual void Update(T entity)
    {
        DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
        if (dbEntityEntry.State == EntityState.Detached)
        {
            DbSet.Attach(entity);
        }  
        dbEntityEntry.State = EntityState.Modified;
    }

Web API方法:

    public HttpResponseMessage Put(MyEditModel editModel)
    {
        var model = editModel.MapToMyEntity();
        _myManager.Update(model);
        return new HttpResponseMessage(HttpStatusCode.NoContent);
    }

更新方法(Update method):
    public void Update(MyEntity model)
    {
        Uow.MyEntities.Update(model);
        Uow.Commit();
    }

在工作的统一性中:
IRepository<MyEntity> MyEntities { get; }

更新实体时,我遇到了以下错误:
附加类型为'X'的实体失败,因为另一个具有相同主键值的实体已经存在。当使用“Attach”方法或将实体的状态设置为“Unchanged”或“Modified”时,如果图形中的任何实体具有冲突的关键值,则可能会发生这种情况。这可能是因为某些实体是新的,并且尚未收到数据库生成的关键字值。在这种情况下,请使用“Add”方法或“Added”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“Unchanged”或“Modified”。
两个解决方法:
1. 如果是调用存储库的第一个方法,更新可以正常工作。(我创建了一个带有ID的实体并调用了更新。)
2. 当您在更新实体之前获取该实体时,更新不起作用。(例如,我获取一个实体X,将其转换为DTO,然后在UI中更改一些值,然后调用Web API以创建具有新值的实体X并调用存储库的更新。)
有什么办法可以避免这个问题吗?当您拥有CRUD应用程序时,总是在更新之前调用获取方法。

我认为Entry所进行的查找是基于.Net引用的,因此出现了这个“问题”。相反,你应该先在Local集合中查找实体。 - Pragmateek
我读到过类似于“从上下文中分离原始实体,然后附加新的/更新的实体”的解决方案,但我不喜欢那个方法。 - Filip
一旦您执行get操作,实体就会加载到上下文中。由于您的更新在get之后失败,因此您必须尝试以某种方式附加重复实体。您可以尝试在将其映射到DTO后,在get之后分离实体吗? - Mister Epic
@Filip,你能发布处理以下内容的 Web API 代码吗?:“然后调用一个 Web API,使用新值创建实体 X 并调用存储库的更新操作。” - Anthony Chu
显示剩余4条评论
2个回答

2
我正在使用自己的附加方法:
public void Attach<E>(ref E entity)
{
    if (entity == null)
    {
        return;
    }

    try
    {
        ObjectStateEntry entry;
        bool attach = false;
        if (ObjectStateManager.TryGetObjectStateEntry(CreateEntityKey(entitySetName, entity), out entry))
        {
            attach = entry.State == EntityState.Detached;

            E existingEntityInCache = (E)entry.Entity;
            if (!existingEntityInCache.Equals(entity))
            {
                existingEntityInCache.SetAllPropertiesFromEntity(entity);
            }
            entity = existingEntityInCache;
        }
        else
        {
            attach = true;
        }
        if (attach)
            objectContext.AttachTo(entitySetName, entity);
    }
    catch (Exception ex)
    {
        throw new Exception("...");
    }
}

0

我曾经遇到过同样的问题。问题出在混合上下文中。当你在context1中从数据库读取实体时,如果你可以使用contex2(具有自己的实体缓存的相同上下文的其他实例)更新此实体,则可能会引发异常。

请检查引用:

通过context1: 从数据库中读取带有引用实体2的实体1

通过context2: 从数据库中读取实体2。然后更新实体1(使用来自context1的引用实体2)。

当您尝试将带有引用实体2的实体1附加到context2时,这会引发异常,因为实体2已经存在于context2中。

解决方案是仅使用一个上下文进行此操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接