模式克服了Entity Framework中缺少ConflictMode.ContinueOnConflict的实现?

时间:2012-09-02 18:04:42

标签: entity-framework-4 batch-processing optimistic-concurrency

Linq To SQL的DataContextSubmitChanges上有一个重载,它允许在抛出Optimistic Concurrency Exception时继续更新,并为开发人员提供一种机制来解决之后{{3 }}。

即使WCFDataServicesContext的{​​{1}}方法都有一个SaveChangedOptions.ContinueOnError参数,至少可以让您在发生错误时继续更新并保留未解决的冲突更新,以便您可以查看它们后面。

(1)为什么SaveChanges方法没有这样的选项?

(2)是否存在任何会模仿Linq To SQL行为的更新模式?我在MSDN上找到的示例使得看起来好像a single Try Catch block会在多次更新的情况下看到您回家。但是这种模式不允许您单独调查每个冲突的更新:它只是提醒您第一次冲突,然后为您提供“在一次扫描中擦除表清理”的选项,以防止任何进一步的乐观并发异常浮出水面,知道是否存在以及你想对它们做些什么。

1 个答案:

答案 0 :(得分:5)

  

为什么ObjectContext.SaveChanges方法没有这样的选项?

我认为最简单的答案是因为Linq-to-Sql,实体框架和WCF数据服务都是由不同的团队实施的,这些团队之间的内部沟通并不像我们希望的那样有效。我已经描述了one of my former answers中较新的API中缺少的一些有趣的功能,但我不认为这是一个缺失的功能 - 我将在我的答案的第二部分解释它。

WCF数据服务具有更多有趣的功能,这些功能也应该是实体框架的一部分。例如:

  

是否存在任何会模仿Linq To SQL行为的更新模式?

有一种模式如何解决这个问题,但你可能不喜欢它。 EF的SaveChanges作为工作单位。它保存所有更改或不保存。如果您有一个场景,您的保存操作可能会导致仅保留部分更改,而不应通过单个SaveChanges调用处理。每个原子变更都应该有自己的SaveChanges调用:

using (var scope = new TransactionScope(...)) {
    foreach (var entity in someEntitiesToModify) {
        try {
            context.SomeEntities.Attach(entity);
            context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
            context.SaveChanges();
        catch (OptimisticConcurrencyException e) {
            // Do something here 

            context.Refresh(e.StateEntries[0].Entity, RefreshMode.ClientWins);
            context.SaveChanges();
        }
    }

    scope.Complete();
}

我认为这个功能不存在的原因是因为它不是通用的,并且正如所提到的那样,它违背了工作单元模式。假设这个例子:

  • 您加载实体
  • 您将新的从属实体添加到已加载实体的导航属性
  • 您在已加载的实体上更改内容
  • 同时其他人同时删除您加载的实体
  • 使用轻松的冲突解决方案触发SaveChanges
  • EF将尝试保存对主体实体的更改,但由于数据库中没有要更新的实体,因此会发生冲突
  • EF将继续,因为冲突解决已经放松
  • EF将尝试插入依赖实体,但它将触发SqlException,因为数据库中不存在主体。此异常将破坏持久性操作,您将不知道为什么它抱怨参照完整性,因为您有一个主体实体。 (有可能这个插入甚至不会发生,EF由于上下文的内部状态不一致而引发另一个异常,但它依赖于EF的内部实现)。

这使冲突解决的整个放松更加复杂。恕我直言有三种解决方法:

  • 根本不支持它。如果您需要按实体进行冲突解决,您仍然可以使用我在上面展示的示例,但对于复杂的情况,它可能无法工作,因为复杂的情况很难解决。
  • 每次发生冲突时重建数据库更改集 - 它意味着探索剩余的更改集,并从处理的持久性中排除与冲突实体及其关系相关的所有实体。存在一个问题:EF无法从处理中排除任何已更改的实体。这将打破工作单元的含义,我再重复一遍:放松冲突解决也会破坏工作单元的意义。
  • 即使主体实体发生冲突,也要让EF继续依赖。这需要处理数据库异常并了解其内容,以了解是否由于主体冲突或由于其他错误(应立即使整个持久性操作失败)而触发异常。在代码级别上理解数据库异常可能非常困难,而且它对于每个受支持的数据库都是特定于提供者的。

这并不意味着它可能无法实现这样的功能,但它需要涵盖关系的所有场景,这可能相当复杂。我不确定Linq-to-Sql是否处理这个问题。

您可以随时在Data UserVoicecheck out the code上提出建议并尝试自行实施。也许我觉得这个功能太复杂了,而且很容易实现。