gpt4 book ai didi

c# - TransactionScope 破坏 SqlConnection 池?

转载 作者:太空狗 更新时间:2023-10-29 23:49:18 25 4
gpt4 key购买 nike

我对 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com