c# - TransactionScope 破坏 SqlConnection 池?

标签 c# sql sql-server transactions

我对 TransactionScope 和异步/同步 SQL 调用有一种奇怪的情况,我很难理解。我希望对这些操作的来龙去脉有更深入了解的人可以阐明这个问题。

情况: 我有一个 NUnit testfixture,它在 [SetUp] 期间创建一个 TransactionScope 并在 [TearDown] 处处理它,让每个测试在相同的数据上运行。我进行了一系列测试,这些测试启动了对数据库的异步操作,然后对数据库执行了同步操作。第一个这样的测试成功完成。第二个这样的测试失败,并显示“已经有一个打开的 DataReader 与此命令关联,必须先关闭它。”。

  1. 如果我完全注释掉 TransactionScope,所有测试都会通过。
  2. 我尝试了各种不同的 TransactionScope 选项和 Complete/Dispose,但出现了同样的问题。
  3. 我在 NUnit 测试 .NET 4.5.1 中使用 Resharper 测试运行器。
  4. 我意识到“正确”的答案可能是“让一切异步等待”。不幸的是,这对我来说不是一个选择。
  5. 我不想启用 MARS,因为这个问题只发生在测试中。
  6. 由于潜在的死锁,我不想使用 GetAwaiter().GetResult()。

在我看来,一旦调用 TransactionScope.Dispose/Complete,自动 SQLConnection 池就无法跟踪哪些连接打开了 DataReader。它将同一个 SqlConnection 分发给两个同时运行的操作,然后第二个就死掉了。

我的主要问题是“是什么导致了这种行为(特别是)?”
我的第二个问题是“有什么办法可以安全地解决这个问题吗?”

下面的复制代码打印出客户端连接 ID。在我的机器上,第二个测试用例中 ASYNC 和 SYNC 调用的 ClientConnectionId 始终相同。

复制代码:

[TestFixture]
public class DataReaderTests
{
    private TransactionScope _scope;
    private string _connString = @"my connection string";

    [SetUp]
    public void Setup()
    {
        var options = new TransactionOptions()
        {
            IsolationLevel = IsolationLevel.ReadCommitted,
            Timeout = TimeSpan.FromMinutes(1)
        };
        _scope = new TransactionScope(TransactionScopeOption.RequiresNew, options, TransactionScopeAsyncFlowOption.Enabled);
    }

    [Test]
    [TestCase("First")]
    [TestCase("Second")]
    public void Test(string name)
    {
        DoAsyncThing().ConfigureAwait(false);
        using (var conn = new SqlConnection(_connString))
        {
            try
            {
                conn.Open();
                Console.WriteLine("SYNC: " + conn.ClientConnectionId);
                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = "SELECT 1";
                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            int id = reader.GetInt32(0);
                        }
                    }
                }
            }
            catch (TransactionAbortedException tax)
            {
                Console.WriteLine("ERROR: " + ((SqlException)tax.InnerException.InnerException).ClientConnectionId);
                throw;
            }
        }
    }

    private async Task DoAsyncThing()
    {
        using (var connection = new SqlConnection(_connString))
        {
            await connection.OpenAsync();
            Console.WriteLine("ASYNC: " + connection.ClientConnectionId);
            using (var cmd = connection.CreateCommand())
            {
                cmd.CommandText = "WAITFOR DELAY '00:02';";
                await cmd.ExecuteNonQueryAsync();
                Console.WriteLine("ASYNC COMPLETE");
            }
        }
    }

    [TearDown]
    public void Teardown()
    {
        _scope.Dispose();            
    }
}`

最佳答案

看看这个 answer

我认为要点是如果没有特殊的连接字符串属性,您不能同时在同一个连接上执行两个事件的 sql 命令。当你在事务作用域下操作时,你应该会发现这两个 SqlConnection 对象具有相同的客户端 ID。但是,如果您删除事务范围,它们就会不同,我认为这意味着它们在不同的连接上运行。

将“MultipleActiveResultSets=true”添加到连接字符串为我解决了这个问题。另一种选择是替换

DoAsyncThing().ConfigureAwait(false); 

DoAsyncThing().ConfigureAwait(false).GetAwaiter().GetResult();

这将在开始第二个命令之前终止第一个命令。

关于c# - TransactionScope 破坏 SqlConnection 池?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50806385/

相关文章:

sql-server - SQL Server 事务表是否应该始终具有代理主键

c# - 如何使用C#验证Powershell .ps1脚本的签名?

sql - 字符串或二进制数据将被截断,字段查找

c# - 获取 ASP.NET Core 中所有已注册的路由

java - 用于替换符号的正则表达式

mysql - 插入这些数据最快的方法是什么

sql - TSQL 尝试清理杂乱的字符串字段并将其转换为小数以执行 SUM 函数

sql-server - Azure 中的服务器端分页 (MS SQL)

c# - 取消带有超时的静态异步函数

c# - 如何通过 C# 连接到 Azure D365 Odata