在 Web 服务中,我正在查询 SQL Server 2016 数据库。使用 .NET TransactionScope 如下以在我的服务层中保持事务管理,但在我的数据层(“存储”类)代码中保持数据查询/命令,我们有几个地方遵循这种模式:
using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
bool needsInsert = await store1.Exists(request.id);
if (needsInsert) mainRowsUpdatedCount = await store2.Insert(request);
transaction.Complete();
}
这些“存储”方法中的每一个都遵循这种模式(使用 Dapper,但我怀疑这无关紧要):const string query = @"SELECT ..."; // or INSERT or MERGE as the case may be
using IDbConnection connection = new SqlConnection(ConnectionString.Value);
return await connection.QueryAsync<T>(query, new { ... }); // or connection.ExecuteAsync as the case may be
这在大多数调用中都很有效,但有时我会得到以下信息(虽然很少):System.PlatformNotSupportedException: This platform does not support distributed transactions.
因此,在上面的示例中,store1.Exists 运行,获取连接,将其登记在事务中,运行其查询,关闭,然后有时在 store2.Insert 可以运行之前,其他一些不相关的线程从连接池中获取相同的连接 已经有一个打开的事务,尝试运行查询并因此抛出 PlatformNotSupportedException,因为 .NET Core(或 .NET 5+)不支持分布式事务?
如果是这样,我怎样才能在不传递我的连接的情况下克服这个问题?
如果不是,还有什么可能导致此异常?
最佳答案
我遇到了同样的问题(也很少),并最终得出结论,它很可能是 .NET Cores 基础库(SqlClient 或事务)中的错误。
据我了解,.NET 事务和本地 SQL 事务之间存在差异。当您创建 TransactionScope
,您可以有效地启动一个新的 .NET 事务,该事务始终可以通过 Transaction.Current
访问。 .调用 .Open()
在新创建的 SqlConnection
上内部检测此环境 .NET 事务并查询内部连接池(存储实际 SQL 连接的位置)是否已与该环境 .NET 事务关联的任何合适的现有连接(即,具有相同的连接字符串)。如果有一个可用(即空闲/空闲),它会直接使用那个而不升级到分布式事务。
因此,如果您的所有代码都在 TransactionScope
中确保始终拥有 最多一个 SqlConnection
随时开放 ,应该保证在幕后重用相同的实际 SQL 连接和本地 SQL 事务,并且不应尝试升级为分布式事务。
在您的示例中,您使用 TransactionScopeAsyncFlowOption.Enabled
确保通过异步代码流正确维护环境事务(即允许在范围内使用 await
)。只要你不打破这个流程,一切都应该没问题。
就算再有TransactionScope
(以及另一个 .NET 事务)在应用程序的某处创建,使用 TransactionScopeAsyncFlowOption.Suppress
,在您的范围之外,未正确处置,并且其关联的线程恰好是在您的范围内继续异步代码流的线程,那么不应该有任何混淆,因为您的下一个 SqlConnection
试图成为泄露的 .NET 事务或您的 .NET 事务的一部分。我不希望以任何方式升级。可能这种情况会在早期(未测试)引发不同类型的异常。
总而言之,我仍然相信这是一些深埋在 .NET 事务意大利面代码中的错误。 ;) 我的问题是我只在生产中的极少数情况下遇到异常,我只是无法使用单元测试或其他东西来重现它。
关于c# - 在 .NET Core/.NET 5+ 中使用 TransactionScope 和 SQL 连接池时如何避免 PlatformNotSupportedException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66679752/