【问题标题】:Can I continue using DbContext after it has thrown an exception?抛出异常后我可以继续使用 DbContext 吗?
【发布时间】:2013-10-01 05:33:47
【问题描述】:

我知道在使用实体框架DbContext 时可能会产生异常的两种不同情况:

  1. 枚举查询(可以抛出EntityCommandExecutionException
  2. 调用SaveChanges(可以抛出DbUpdateException

DbContext 的单个实例中,我想捕获这些异常,如果适用,尝试恢复,然后重复操作。

具体来说,如果对SaveChanges 的调用由于死锁而引发异常,我想重试对SaveChanges 的调用。我已经知道如何检测这种情况并执行重试。

我在这里看到this answer,表示死锁后不应该使用SQL连接。这表明我应该重新启动整个DbContext 和更高级别的操作以从此类异常中恢复。

我不确定在DbContext 引发此类异常后继续使用它是否安全。它会进入无法使用的状态吗?它仍然可以工作但不能正常工作吗? SaveChanges 将不再以事务形式出现吗?

【问题讨论】:

  • MSDN上有一个例子,这里修复数据后第二次调用SaveChangesmsdn.microsoft.com/en-us/library/bb896255.aspx
  • @Colin;谢谢,这是一个很好的观点。该示例与我的示例之间的区别在于我的示例基于数据库服务器引发的错误,而该示例基于 Entity Framework 为响应受影响的行数而生成的异常。
  • 在我看来你会没事的。这告诉你事务在 EF5 和 6 中是如何工作的:msdn.com/data/dn456843 我建议你做一个单元测试

标签: entity-framework entity-framework-5


【解决方案1】:

如果您没有为DbContext 提供已打开的SQL 连接,则DbContext 将在您调用SaveChanges 时为您打开和关闭连接。在这种情况下,保留DbContext 没有任何危险,当然DbContext 持有的实体可能处于无效状态(因为这可能是引发SQL 异常的原因)。

这是一个由打开的 SQL 连接和事务提供的 DbContext 示例:

using (var connection = new SqlConnection("my connection"))
{
    connection.Open();

    using (var transaction = connection.BeginTransaction())
    {
        using (var context = new DbContext(connection))
        {
            // Do useful stuff.

            context.SaveChanges();
        }

        transaction.Commit();
    }
}

如果您为DbContext 提供在事务上下文中运行的SqlConnection,则this answer 保留。

请注意,Entity Framework 不会创建嵌套事务。它只是检查连接是否“在用户事务中登记”。如果SaveChanges 已经在事务中运行,则不会启动任何事务。但是,Entity Framework 无法检测数据库是否由于严重故障(例如数据库死锁)而中止了事务。因此,如果对 SaveChanges 的第一次调用失败并出现死锁之类的问题,并且您捕获并调用 SaveChanges,实体框架仍然认为它在事务中运行。

这意味着第二次调用是在没有事务的情况下执行的,这意味着当操作中途失败时,已经执行的语句将不会回滚,因为没有事务可以回滚。

如果 Entity Framework 使用嵌套事务,可以防止 SaveChanges 操作撕裂的问题,但它仍然不能解决一般的一致性问题。

当我们没有明确提供它们时,Entity Framework 会为我们创建连接和事务。当对SaveChanges 的调用是更大的整体事务的一部分时,我们只需要/想要显式地提供连接和事务。因此,即使 EF 为我们创建了一个嵌套事务并在从SaveChanges 返回之前提交了它,如果我们再次调用SaveChanges,我们就会遇到麻烦,因为这个“嵌套”事务实际上根本没有嵌套。当 EF 提交这个“嵌套”事务时,它实际上提交了唯一的事务,这意味着我们需要成为原子的整个操作都被破坏了; SaveChanges 所做的所有更改都已提交,而在此调用之后可能发生的操作未运行。显然这不是一个好地方。

这个故事的寓意是,要么让 Entity Framework 为你处理连接和事务,你可以重做对 SaveChanges 的调用而没有风险,或者你自己处理事务并且在数据库抛出异常时必须快速失败;你不应该再打电话给SaveChanges

【讨论】:

  • 您能否澄清“为DbContext 提供在事务上下文中运行的SqlConnection”的意思。我的理解是SaveChanges 在内部创建了一个事务(stackoverflow.com/a/6028691/150342)。所以我会认为再次调用它并不意味着数据库操作将“在无事务上下文中执行”
  • Aaaah...OK....现在,对SaveChanges 的调用会导致嵌套事务吗?如果出现死锁,那将回滚。然后您可以捕获异常并重试SaveChanges - 新的嵌套事务 - 无需担心处于无事务上下文中?
  • 我尝试对此进行测试,但我一调用 SaveChanges 就遇到了异常。在 EF5 中,您无法为 DbContext 提供已打开的 SQL 连接,因此无法开始事务。 EF6 引入了更细粒度的控制msdn.com/data/dn456843
  • DbContext 类包含一个接受现有连接的constructor。虽然我从未对此进行过测试,但我发现这不太可能在 DbContext 上不起作用,而在 ObjectContext 上起作用。
  • 我认为传入关闭的连接对于防止事务升级为分布式事务很有用,这种情况发生在DbContext 在事务期间启动第二个连接时。
猜你喜欢
  • 1970-01-01
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-09
  • 1970-01-01
  • 2017-10-06
  • 1970-01-01
相关资源
最近更新 更多