Entity framework 将实体附加到上下文失败,因为它已存在

Entity framework 将实体附加到上下文失败,因为它已存在,entity-framework,repository-pattern,Entity Framework,Repository Pattern,我使用CodeCamper的统一工作和通用存储库 要更新实体,一般回购协议有: public virtual void Update(T entity) { DbEntityEntry dbEntityEntry = DbContext.Entry(entity); if (dbEntityEntry.State == EntityState.Detached) { DbSet.Attach(entity);

我使用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);
    }
更新方法:

    public void Update(MyEntity model)
    {
        Uow.MyEntities.Update(model);
        Uow.Commit();
    }
在工作单元中:

IRepository<MyEntity> MyEntities { get; }
i阳性髓质{get;}
更新实体时,我收到以下错误:

其他信息:附加“X”类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。 如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“Unchanged”或“Modified”时可能会发生这种情况。 这可能是因为某些实体是新的,尚未收到数据库生成的键值。 在这种情况下,使用“添加”方法或“添加”实体状态跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”

  • 当更新是您调用存储库的第一个方法时,它可以正常工作。 (我创建了一个id已经在DB中的实体,并调用了更新。)

  • 当您在更新实体之前获取实体时,更新不起作用。 (例如,我得到一个实体X,将其转换为DTO,然后在UI中更改一些值, 然后调用一个web api,该api使用新值和 调用存储库的更新。)

  • 有什么办法避免这种情况吗?
    当你有一个CRUD应用程序时,你总是在更新之前调用get

    我也有同样的问题。问题的背景是复杂的。当您在context1中从DB读取实体时。然后,如果您可以使用contex2(具有自己的实体缓存的相同上下文的其他实例)更新此实体。这可能引发异常

    请检查参考资料:

    按上下文1: 从数据库中读取具有引用的entity2的entity1

    按上下文2: 从数据库读取entity2。然后更新entity1(使用context1中引用的entity2)

    当您尝试将引用了entity2的entity1附加到context2时,会引发异常,因为context2中已经存在entity2


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

    我正在使用自己的附加方法:

    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("...");
        }
    }
    
    公共作废附加(参考E实体)
    {
    if(实体==null)
    {
    返回;
    }
    尝试
    {
    ObjectStateEntry条目;
    bool-attach=false;
    if(ObjectStateManager.TryGetObjectStateEntry(CreateEntityKey(entitySetName,entity),out条目))
    {
    attach=entry.State==EntityState.Detached;
    E existingEntityInCache=(E)entry.Entity;
    如果(!existingEntityInCache.Equals(实体))
    {
    existingEntityInCache.SetAllPropertiesFromEntity(实体);
    }
    实体=现有实体incache;
    }
    其他的
    {
    附加=真;
    }
    如有的话(附上)
    AttachTo(entitySetName,entity);
    }
    捕获(例外情况除外)
    {
    抛出新异常(“…”);
    }
    }
    
    我认为
    Entry
    所做的查找是基于.Net引用的,因此出现了“问题”。相反,您应该首先尝试在
    本地
    集合中查找实体。我读过类似“在上下文中分离原始实体,然后附加新的/更新的实体”的内容,但我不喜欢这种解决方案。只要执行
    获取
    ,实体就会加载到您的上下文中。由于更新在
    获取
    后失败,因此您必须尝试以某种方式附加重复的实体。一旦您将实体映射到DTO,您是否可以尝试在
    get
    之后分离实体?@Filip您是否可以发布处理此问题的web api代码:“然后调用web api,使用新值创建实体X并调用存储库的更新”?数据库中的主键是否设置为自动增量?