TransactionScope中的DbContext.SaveChanges()可见性

时间:2018-06-14 10:36:55

标签: c# entity-framework dbcontext transactionscope

鉴于TransactionScope有2个随后打开的DbContexts,第一个上下文保存的更改是否保证在第二个上下文的范围内可见?

var txOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted };
using (var transaction = new TransactionScope(TransactionScopeOption.Required, txOptions))
{
    using (var context1 = new MyDbContext())
    {
        context1.Employees.Single(e => e.Id == 1).Salary += 1; // Update
        context1.Employees.Remove(context1.Employees.Single(e => e.Id == 2)); // Delete
        context1.Employees.Add(new Employee { Id = 3 }); // Add

        context1.SaveChanges();
    }

    using (var context2 = new MyDbContext())
    {
        // are changes saved by context1 guaranteed to be visible here?
    }

    transaction.Complete();
}

通常我会期待它们,但后来我看到“No, this was a coincidence because the 2nd context reused the connection of the 1st from the connection pool. This is not guaranteed and will break under load.”让我感到困惑。有人可以确认或反驳这个吗?

1 个答案:

答案 0 :(得分:1)

My understandingall efforts将确保您的线程从池接收相同的连接,前提是您在请求另一个连接之前关闭每个连接,并且连接字符串是相同的。重复使用相同的连接将阻止事务升级到DTC。

但是,如果您仍需要进一步的保证,则会有overload of the DbContext构造函数接受现有连接。这样,您就可以保证两个上下文使用相同的Connection。这样您就不必担心轻量级事务管理器的行为。 IMO您最好打开自己的连接,并将此传递给两个上下文,并将contextOwnsConnection标志设置为false。

(希望下面的内容都是假设的)

不重复使用连接的后果非常糟糕 - 如果context2

} context1,那么边界TransactionScope会升级进入分布式事务,这在任何情况下都会导致进一步的潜在锁定+死锁问题。

即。在您的示例中,与context1中已修改员工关联的3行将被阻止到context2上具有ReadCommited隔离级别的第二个数据库连接,直到提交事务为止,或者回滚(即您的程序可能会挂起,直到事务或命令超时)。

我想一个亟待解决的问题 - 因为两个上下文都使用相同的数据库,如果可能的话,尝试组合这两个上下文。通过这种方式,您可以完全避免绑定TransactionScope - SaveChanges()充当针对单个连接的单阶段事务。