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

Dab*_*rnl 2 batch-processing optimistic-concurrency entity-framework-4

Linq To SQL DataContext有一个重载,SubmitChanges它允许在抛出Optimistic Concurrency Exception时继续更新,并为开发人员提供一种机制,以便在一个Try Catch块中解决之后的冲突.

甚至它的方法也WCFDataServicesContext有一个SaveChangedOptions.ContinueOnError参数,它 SaveChanges至少允许你在发生错误时继续更新并且保留未解决的冲突更新,以便你以后可以查看它们.

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

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

Lad*_*nka 5

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

我认为最简单的答案是因为Linq-to-Sql,实体框架和WCF数据服务都是由不同的团队实施的,这些团队之间的内部沟通并不像我们希望的那样有效.我在之前的一个答案中描述了新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();
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

您可以随时在Data UserVoice上提出建议或查看代码并尝试自己实现.也许我觉得这个功能太复杂了,而且很容易实现.