c# - 在 .NET Core/.NET 5+ 中使用 TransactionScope 和 SQL 连接池时如何避免 PlatformNotSupportedException

标签 c# .net-core sqlconnection transactionscope system.data.sqlclient

在 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/

相关文章:

c# - Linq 到 SQL 连接

c# - SqlConnection连接数据的替代方案是什么?

c# - 当前单元格不能设置为datagridview中的不可见单元格

c# - 如何为我们在服务器上开发和运行的应用程序配置 log4net?

c# - .net 中可以读取的文件的最大大小

.net - 在 sles 11 中运行 .NET

c# - 如何在 MVC 中托管多个 IoC 驱动的 WCF 服务?

c# - 使用 dotnet 发布时设置 exe 文件版本

c# - Serilog 不将日志写入文件

c# - Db Connection.State Change 什么时候调用?