【问题标题】:How to revert changes on detached entities如何还原分离实体上的更改
【发布时间】:2016-03-12 11:37:18
【问题描述】:

这是程序的流程:

  1. 获取实体列表并使用它们。这会将所有实体与上下文断开/分离。
  2. 对其中一个实体进行更改并保存。我正在从上下文加载实体,并将分离实体的更改(标量属性和关系)应用到新加载的实体。
  3. 我有一个功能,用户可以恢复对断开连接的实体所做的所有更改。这是我正在使用的代码:

        public async Task RevertChanges()
    {
        using (var db = new TwinTailDb())
        {
            //Fansubs.Clear();
    
            if (db.Entry(this).State == EntityState.Detached && Id != 0)
            {
                db.ArchiveEntries.Attach(this);
                await db.Entry(this).ReloadAsync();
            }
    
            //await db.Entry(this).Collection(a => a.Fansubs).LoadAsync();
        }
    }
    

但是,当我附加分离的实体时,它会抛出此异常:

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

请注意,上下文是自动释放的,因为我使用的是 using 语句。

我不确定为什么即使主键存在冲突,因为我什至没有加载另一个实体,因为之前的上下文已经被释放了。

此外,如果我跳过保存实体中所有更改的步骤 2,它不会引发异常。我一直在想,不知何故它仍在被跟踪。

编辑: 以下是我跳过附加时发生的情况,证明实体确实已分离。

附加信息:无法为“ArchiveEntry”类型的实体调用成员“ReloadAsync”,因为该实体在上下文中不存在。要将实体添加到上下文中,请调用 DbSet 的 Add 或 Attach 方法。

说真的,发生了什么:(

【问题讨论】:

  • 您是否在第 2 步中处理上下文?
  • 好吧,因为我使用的是“使用”语法,所以它会自动处理。
  • 通过 PK 查询持久化实体然后从那时起使用它不是最简单的吗?或者您是否专门尝试恢复为实体之前的确切值,而不是当前的持久值?
  • 我已经想到了,但我不希望其他对象保留对旧实体的引用。在架构方面,我想保持相同的参考(更少的移动部件)。是的,我正在尝试将其恢复为持久值。
  • 我觉得这与“还原”没有任何关系,而更多的是附加一个分离的实体,所以我发布了另一个问题:stackoverflow.com/questions/35987384/…我应该删除这个还是保持这个开放别人看?

标签: c# .net entity-framework


【解决方案1】:

涉及上下文的实体上的某些操作可能会将状态更改为已附加。它可能在将实体传递给上下文中的方法时发生。尝试在实体状态更改时放置一个断点,并在实际调用附加之前确保该实体没有被附加,作为其他操作的副作用。如果是这种情况,那么您正在尝试附加一个已经附加的实体,这应该会导致异常。

【讨论】:

  • 我也尝试捕获异常并检查其状态。据说即使在异常之后它也是分离的。此外,上面发布的代码是我在实体上调用的整个函数。我很确定它总是分离的,因为我确保为每个操作处理上下文。
  • 能否请您提供更多有关包含 RevertChanges 方法的对象是如何实例化的上下文,它的生命周期是多少,以及它是否长期存在并且可能从多个线程中调用? RevertChanges 任务是否可能与其他一些在 this 实例上运行并与上下文一起工作的任务并行运行。也是TwinTailDb是EF T4模板直接生成的,还是一个封装了DbContext并增加了一些逻辑的类?
  • 抱歉。是的,TwinTailDb 是一个生成的 DbContext(继承)及其模型。我为每个模型编写了部分类,以便更容易调用。 RevertChanges 就是其中之一。每个函数都对上下文执行操作,但在函数结束后立即安全地释放。实体在第一个 Window 上实例化,因为它显示所有实体。我所做的唯一任务是我在问题中列出的 3 个步骤。我敢肯定这三件事不会同时发生。在我的应用程序中,用户只能选择保存或关闭(恢复更改)编辑窗口。
  • 续。大多数时候,我使用 UI 上下文等待一个任务(通过在异步 WPF 事件处理程序中等待)。还有其他时候(如在步骤 1 中)我使用不同的上下文(使用 Task.Run)执行操作。我假设这无关紧要,因为我并没有真正并行运行它们。目前,我所有的任务都在一个接一个地运行。昨天我怀疑这是一个问题,所以我在 Save and Revert 中添加了一些 Console.WriteLine。 100% 的时间保存任务在我可以调用 RevertChanges 之前完成。
【解决方案2】:

回答我自己的问题。

主要问题是我处理上下文生命周期的方式是错误的。此外,附加从另一个上下文加载的实体肯定会引发错误。在上下文中加载的实体只能在该上下文中使用。

我的上下文太短暂了。我将调整它的生命周期,以便每个事务(数据库进程)都有一个上下文。

这是一篇关于如何解决/设计架构的非常详细的文章:http://mehdi.me/ambient-dbcontext-in-ef6/

修复架构后,重新加载/恢复实体可以简单地通过调用来完成:

DbContext.Entry(entity).Reload();

【讨论】:

    猜你喜欢
    • 2023-04-09
    • 2020-07-30
    • 2012-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多