.NET 6中PostgreSQL连接池配合Transaction Scope失效问题求助
问题:TransactionScope中使用Npgsql+Dapper触发预准备事务禁用错误
问题描述
在.NET 6应用中使用Npgsql和Dapper连接PostgreSQL,连接字符串已启用连接池,所有命令在TransactionScope内执行,每次通过NpgsqlDataSource.OpenConnection新建连接。调用transactionScope.Complete()时抛出错误:
System.Transactions.TransactionAbortedException : 事务已中止。
---- Npgsql.PostgresException : 55000: prepared transactions are disabled
原本期望连接池会复用同一个连接,避免触发分布式事务,但实际不符合预期,需排查问题。
代码示例
using (var transactionScope = new TransactionScope()) { using connection1 = NpgsqlConnection(); await connection1.ExecuteAsync("procedure1"); using connection2 = NpgsqlConnection(); await connection2.ExecuteAsync("procedure2"); transactionScope.Complete(); } private static NpgsqlConnection NpgsqlConnection() { string connectionString = "User ID=postgres;Password=*****;Host=hostName;Port=5432;Database=DBTestPostgres;Pooling=true;"; NpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); using NpgsqlDataSource dataSource = dataSourceBuilder.Build(); NpgsqlConnection conn = dataSource.OpenConnection(); return conn; }
错误原因
核心问题在于每次创建连接时都新建并释放NpgsqlDataSource实例。Npgsql的连接池与NpgsqlDataSource绑定,每个DataSource拥有独立的连接池。你在NpgsqlConnection()方法中每次创建新的DataSource并立即释放,导致每次获取的连接属于不同的连接池,完全无法复用。
当TransactionScope内打开第二个连接时,由于两个连接分属不同连接池,Npgsql无法复用同一连接,只能将事务升级为分布式事务。而PostgreSQL默认禁用预准备事务,因此抛出错误。
解决方案
1. 复用NpgsqlDataSource实例
NpgsqlDataSource是线程安全的,应作为单例在应用生命周期内复用,而非每次创建连接时新建。修改后的代码如下:
// 全局复用的DataSource实例,应用启动时初始化一次 private static readonly NpgsqlDataSource _dataSource; static YourClassName() { string connectionString = "User ID=postgres;Password=*****;Host=hostName;Port=5432;Database=DBTestPostgres;Pooling=true;"; NpgsqlDataSourceBuilder dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString); _dataSource = dataSourceBuilder.Build(); } private static NpgsqlConnection NpgsqlConnection() { return _dataSource.OpenConnection(); } // 原TransactionScope代码补充异步流支持 using (var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { using var connection1 = NpgsqlConnection(); await connection1.ExecuteAsync("procedure1"); using var connection2 = NpgsqlConnection(); await connection2.ExecuteAsync("procedure2"); transactionScope.Complete(); }
2. 关键补充说明
- 必须为
TransactionScope添加TransactionScopeAsyncFlowOption.Enabled参数,否则异步方法会破坏事务上下文。 - 若确需使用分布式事务,需在PostgreSQL配置文件
postgresql.conf中设置max_prepared_transactions为大于0的值(如100),但这并非最优解,复用连接池才是符合设计预期的做法。
内容的提问来源于stack exchange,提问作者Hari
相关产品推荐
相关产品推荐

