Npgsql 4.x 对 System.Transactions.TransactionsScope 的支持不起作用?

Npgsql 4.x support for System.Transactions.TransactionsScope not working?

我们有一个基于 Web 的多层 eHealth 系统平台,该平台使用 class/object 层次结构。例如,一个"patient clinic visit"对象可能由多个诊断对象、多个用药对象、多个观察对象、化验对象等组成。使用 .NET System.Transactions.TransactionScope.

处理数据库事务

将数据持久化到数据库时,层次结构中的每个嵌套对象:

  1. 实例化一个 TransactionScope(使用默认事务 必需选项)
  2. 实例化一个 NpgsqlConnection 连接
  3. 有自己的 SQL 和
  4. 设置transactionsScope.Complete(),如果一切顺利的话

为简化起见,对象层次结构中的对象就像下面的代码示例一样:

void RootMethod()
{
    using(TransactionScope scope = new TransactionScope())
    {
        /* Perform transactional work here */
        SomeMethod();
        AnotherMethod();

        scope.Complete();
    }
}

void SomeMethod()
{
    using(TransactionScope scope = new TransactionScope())
    {
        using(NpgsqlConnection connection = new NpgsqlConnection(connectionString))
        {
            connection.Open()
            /* Perform transactional work here */
            scope.Complete();
        }
    }
}

void AnotherMethod()
{
    using(TransactionScope scope = new TransactionScope())
    {
        using(NpgsqlConnection connection = new NpgsqlConnection(connectionString))
        {
            connection.Open()
            /* Perform transactional work here */
            scope.Complete();
        }
    }
}

封装在 "using(TransactionScope scope = new TransactionScope()) {...}" 代码块中的程序代码在 Oracle 上(使用 db Oracle 驱动程序)被纳入同一事务中,但在 Postgres 上使用 Npgsql 单独的事务似乎得到生成 而不是一笔交易。

因此,Postgres 上的事务因外键约束而失败 - 数据无法保存在子表中,因为创建的单独事务看不到在数据之前插入父表(在单独的事务中)的数据由父对象提交。

我们在 Npgsql connectString 中设置了 Enlist=true 并且服务器参数 max_prepared_transactions 在我们的 Postgres 服务器上设置为比 0 值更大的数字。

我们测试过的Npgsql驱动版本是4.0.7和4.1.2。我们开发环境中的 Postgres 服务器版本是版本 10.

Npgsql 文档 https://www.npgsql.org/doc/transactions.html 说 System.Transactions.TransactionScope 受支持(自 v3.3 以来一直如此),我们在 Whosebug 上搜索的其他 Npgsql 相关答案也是如此。

乍一看,Npgsql unit tests 似乎在事务单元测试中使用了一个数据库连接。

问题:

如上所述,使用 Npgsql 4.x "using(TransactionScope scope = new TransactionScope()) {...}" 代码块中的每个打开的数据库连接似乎都会生成一个新事务,而不是在同一个事务中登记。

在内部 TransactionScope 中打开第二个 NpgsqlConnection 之前,需要关闭在外部 TransactionScope 中打开的 NpgsqlConnection(以允许 Npgsql 在内部重用相同的物理连接,而不会升级到分布式事务)。