如果您不提供DbContext
已打开的 SQL 连接,则DbContext
当您调用SaveChanges
. 在这种情况下,保持周围没有任何危险DbContext
,当然,除了DbContext
持有的实体可能处于无效状态(因为这可能是引发 SQL 异常的原因)。
这是一个DbContext
由打开的 SQL 连接和事务提供的示例:
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();
}
}
如果您提供在事务上下文中运行的 a,则DbContext
此答案成立。SqlConnection
请注意,实体框架不会创建嵌套事务。它只是检查连接是否“在用户事务中登记”。如果SaveChanges
已经在事务中运行,则不会启动事务。但是,Entity Framework 无法检测数据库是否由于严重故障(例如数据库死锁)而中止了事务。因此,如果第一次调用SaveChanges
失败并出现死锁之类的问题,并且您捕获并回忆SaveChanges
,Entity Framework 仍然认为它在事务中运行。
这意味着第二次调用在没有事务的情况下执行,这意味着当操作中途失败时,已经执行的语句将不会回滚,因为没有事务可以回滚。
如果 Entity Framework 使用嵌套事务,则可以防止撕裂SaveChanges
操作的问题,但它仍然不能解决一般的一致性问题。
当我们没有明确提供它们时,实体框架会为我们创建连接和事务。SaveChanges
当调用是更大的整体事务的一部分时,我们只需要/想要明确地提供连接和事务。因此,即使 EF 为我们创建了一个嵌套事务并在从 返回之前提交了它SaveChanges
,如果我们第二次调用,我们也会遇到麻烦SaveChanges
,因为这个“嵌套”事务实际上根本没有嵌套。当 EF 提交这个“嵌套”事务时,它实际上提交了唯一的事务,这意味着我们需要成为原子的整个操作都被破坏了;by 完成的所有更改SaveChanges
都已提交,而在此调用之后可能发生的操作未运行。显然,这不是一个好去处。
这个故事的寓意是,要么让 Entity Framework 为你处理连接和事务,你可以SaveChanges
毫无风险地重做调用,或者你自己处理事务,当数据库抛出异常时必须快速失败;你不应该再打电话SaveChanges
了。