2

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

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

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

(2) 是否存在任何模仿 Linq To SQL 行为的更新模式?我在 MSDN 上找到的示例使它看起来好像单个 Try Catch 块会在多次更新的情况下让您回家。但是这种模式不允许您单独调查每个有冲突的更新:它只是提醒您注意第一个冲突,然后让您选择“一次性清除表”以防止出现任何进一步的乐观并发异常,而无需您的知道是否存在,以及您希望如何处理它们。

4

1 回答 1

5

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

我认为最简单的答案是因为 Linq-to-Sql、Entity Framework 和 WCF 数据服务都是由不同的团队实现的,这些团队之间的内部沟通并没有像我们希望的那样工作。我在以前的一个答案中描述了新 API 中缺少的一些有趣功能,但我不认为这是一个缺少的功能 - 我将在答案的第二部分中解释它。

WCF 数据服务有更多有趣的特性,它们也应该是实体框架的一部分。例如:

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

有一种模式可以解决这个问题,但你可能不会喜欢它。EFSaveChanges作为工作单元工作。它要么保存所有更改,要么不保存。如果您的保存操作可能会导致仅保留部分更改而不是单个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 UserVoice提出建议或查看代码并尝试自己实现。也许我觉得这个功能太复杂了,很容易实现。

于 2012-09-02T20:20:07.383 回答