TransactionScope 未按预期工作是否存在某些根本原因?

Mar*_*ith 5 sql-server ado.net azure-sql-database

对于以下代码...

using System.Transactions;
using Microsoft.Data.SqlClient;

var connectionString = "...";


using (var scope = new TransactionScope(TransactionScopeOption.RequiresNew,
           new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot }))
{
    TestExec(connectionString);
    TestExec(connectionString);
    scope.Complete();
}


TestExec(connectionString);

static void TestExec(string connectionString)
{
    using var conn = new SqlConnection(connectionString);
    conn.Open();
    var cmd = new SqlCommand
    {
        CommandText = @"
DECLARE @useroptions table (Opt varchar(50), Value varchar(50));
INSERT @useroptions EXEC ('dbcc useroptions')
SELECT Value AS [isolation level], CURRENT_TRANSACTION_ID() AS [tran_id], @@SPID FROM @useroptions WHERE Opt = 'isolation level'",
        Connection = conn
    };

    using var reader = cmd.ExecuteReader();
    while (reader.Read())
        Console.WriteLine($"Isolation Level:{reader.GetString(0)} TransactionId:{reader.GetInt64(1)}, Session:{reader.GetInt16(2)}");
}
Run Code Online (Sandbox Code Playgroud)

当连接字符串用于本地 SQL Server 时,典型输出如下

隔离级别:快照 TransactionId:2696760,会话:57
隔离级别:快照 TransactionId:2696760,会话:57
隔离级别:快照 TransactionId:2696821,会话:57

当连接字符串用于 Azure SQL 数据库时,典型输出为...

隔离级别:快照 TransactionId:4636394,会话:92
隔离级别:读取提交的快照 TransactionId:4636394,会话:92
隔离级别:读取提交的快照 TransactionId:4636446,会话:92

这两种输出都不是真正所希望的。

在“本地”情况下,当范围结束且新事务开始时,隔离级别不会重置。这引起了许多抱怨(例如这个 StackOverflow 问题)。

在“SQL 数据库”情况下,隔离级别会重置,但发生得太早,并且不会在整个范围内维护。我遇到了这种行为,因为在我的情况下,第一个查询执行读取,第二个查询执行写入,并且代码依赖于从未出现的乐观并发异常(因为写入查询没有按预期在快照上运行)。

看起来“本地”行为暂时与 SQL 数据库行为相同,但已回滚此处报告了第二个错误。

谁能解释一下这个问题?

  • 行为的差异是由于服务器(的sp_reset_connection)或客户端的不同实现造成的吗?
  • 如果是服务器端,这是否是一个有意的设计决策,旨在根据 KB3025845 在本地解决问题,但让 Azure 表现不同?
  • 如果这是一个深思熟虑的设计决定,是否存在一些根本原因导致我们无法让这两种情况按预期工作?

J.D*_*.D. 2

我猜您可能正在从对事情为何如此的内部知识有更多了解的人那里寻找更合格的答案。不幸的是我不是那个人。

\n

但如果您还没有遇到过,这种行为(针对 SQL Server 产品)已被正式记录,并且在我看来,微软将其视为设计行为(即使它最初是某种错误),如下所示:在 SQL Server 中的快照隔离 - 使用隔离级别管理并发中提到:

\n
\n

隔离级别具有连接范围的范围,并且一旦使用 SET TRANSACTION ISOLATION LEVEL 语句为连接设置,它就会一直有效,直到连接关闭或设置另一个隔离级别。当连接关闭并返回到池中时,将保留最后一个 SET TRANSACTION ISOLATION LEVEL 语句的隔离级别。重用池连接的后续连接将使用连接池时有效的隔离级别。

\n
\n

FWIW,Kendra Little 在如何在 RCSI 和快照隔离级别之间进行选择中SNAPSHOT简要介绍了使用隔离级别时的这个具体问题时的这个具体问题:

\n
\n

这通常意味着您需要将您喜欢使用快照的查询分段到它们自己的连接池中,因为重置连接不会同时重置隔离级别。

\n
\n