为什么此TransactionScope在完成第一个请求之前不阻止后续请求

本文关键字:请求 为什么 TransactionScope 第一个 | 更新日期: 2023-09-27 18:26:23

我已经研究事务两天了,也许在获取了这么多信息后,我遗漏了一些明显的东西。这里的目标是阻止同时请求。如果condition为真,则插入数据,之后condition将为假。同时请求都将在插入数据之前检查condition,然后都将尝试插入数据。

public async Task<ActionResult> Foo(Guid ID)
{
    Debug.WriteLine("entering transaction scope");
    using (var transaction = new TransactionScope(
        TransactionScopeOption.Required,
        new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable },
        TransactionScopeAsyncFlowOption.Enabled
        ))
    {
        Debug.WriteLine("entered transaction scope");
        var context = new DbContext();
        Debug.WriteLine("querying");
        var foo = context.Foos.FirstOrDefault(/* condition */);
        Debug.WriteLine("done querying");
        context.Foos.Add(new Foo());
        /* async work here */
        Debug.WriteLine("saving");
        context.SaveChanges();
        Debug.WriteLine("saved");
        Debug.WriteLine("exiting transaction scope");
        transaction.Complete();
        Debug.WriteLine("exited transaction scope");
        return View();
    }
}

这是使用Fiddler同时执行两个请求时的调试输出:

进入交易范围输入的交易范围查询已完成查询进入交易范围输入的交易范围查询已完成查询节省物节省物已保存System.Data.dll中首次出现"System.Data.SqlClient.SqlException"类型的异常退出事务范围已退出事务范围

这是我对代码应该如何工作的理解:

  • 需要具有可串行隔离级别的事务,以防止幻影读取
  • 我无法使用DbContext.Database.Connection.BeginTransaction,执行查询时会引发错误:

    当分配给命令的连接处于挂起的本地事务中时,ExecuteReader要求命令具有事务
    命令的Transaction属性尚未初始化。

  • TransactionScope在使用基于任务的异步时通常会导致问题。http://entityframework.codeplex.com/discussions/429215但是,.NET 4.5.1添加了额外的构造函数来处理此问题。如何在可取消的async/await中处理TransactionScope
  • EF 6改进了交易支持,但我仍然使用EF 5。http://msdn.microsoft.com/en-us/data/dn456843.aspx

很明显,它并没有像我希望的那样工作。使用TransactionScope有可能实现我的目标吗?还是我必须升级到EF 6?

为什么此TransactionScope在完成第一个请求之前不阻止后续请求

序列化无法解决旧的"先检查后插入"问题。两个可序列化的事务可以同时评估条件,得出必须插入的结论,然后两个事务都尝试插入,只有一个失败,一个成功。这是因为所有读取都在可串行隔离下相互兼容(事实上,它们在所有隔离级别下都兼容)。

如何解决这个问题有很多学派。有些人建议使用MERGE。有些人建议在检查查询中使用锁提示来获取X或U锁。就我个人而言,我建议始终插入并优雅地恢复重复密钥冲突。执行的另一种方法是使用显式应用程序锁。

EF或System。交易实际上只会给这个问题增加噪音。从根本上讲,这是一个后端SQL问题。至于如何在线程之间流动事务范围的问题,请参阅Get-TransactionScope以使用async/await(显然,您已经知道了这一点,从阅读OP中可以看出…我没有在第一次读取中注册)。您需要这样才能让异步代码在适当的上下文中登记,但阻塞/锁定仍然是根本的后端问题。