我知道在使用实体框架DbContext
时可以产生异常的两种不同情况:
EntityCommandExecutionException
)SaveChanges
(可以抛出DbUpdateException
)在DbContext
的单个实例中,我想要捕获这些异常,尝试恢复(如果适用),然后重复操作。
具体来说,如果对SaveChanges
的调用因死锁而引发异常,我想重试SaveChanges
的调用。我已经知道如何检测这种情况并执行重试。
我在这里看到this answer,表示在死锁后不应使用SQL连接。这表明我应该重新启动整个DbContext
和更高级别的操作以从这些异常中恢复。
我不确定的是,在引发此类异常之后继续使用DbContext
是否安全。它会进入无法使用状态吗?它仍然可以工作但功能不正确吗?事件SaveChanges
不会再发生了吗?
答案 0 :(得分:6)
如果您未向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
已在事务中运行,则不会启动任何事务。但是,实体框架无法检测数据库是否因严重故障(例如数据库死锁)而中止了事务。因此,如果对SaveChanges
的第一次调用失败并出现类似死锁的情况,并且您捕获并回忆SaveChanges
,则实体框架仍然认为它在事务中运行。
这意味着第二次调用在没有事务的情况下执行,这意味着当操作中途失败时,由于没有要回滚的事务,因此不会回滚已经执行的语句。
如果Entity Framework使用嵌套事务,则可能会阻止SaveChanges
操作被破坏的问题,但它仍然无法解决一致性的一般问题。
当我们不明确提供它们时,实体框架会为我们创建连接和事务。当SaveChanges
的调用是更大的整体事务的一部分时,我们只需要/想要显式提供连接和事务。因此,即使EF为我们创建了一个嵌套事务并在从SaveChanges
返回之前提交了这个事务,如果我们第二次调用SaveChanges
,我们就会遇到麻烦,因为这个'嵌套'事务实际上并不是嵌套的一点都不当EF提交这个'嵌套'事务时,它实际上提交了唯一的事务,这意味着我们需要原子的整个操作被撕裂; SaveChanges
完成的所有更改都已提交,而此调用后可能发生的操作未运行。显然这不是一个好地方。
故事的寓意在于,要么让Entity Framework为您处理连接和事务,您可以无风险地重做对SaveChanges
的调用,或者您自己处理事务,并且在数据库抛出时必须快速失败例外;你不应该再次致电SaveChanges
。