TransactionScope:具有不同数据库连接的嵌套事务SQL Server 和 Postgresql

问题描述

我正在使用 NpgsqlConnection 编写一个带有事务的 SDK 方法供其他人使用。

当他们调用我的方法时,他们使用 sqlConnection 和另一个事务来包装他们的数据库内容和我的 SDK 的数据库内容

如果我在没有事务的情况下设置我的 SDK 方法,则外部代码很好,我的 SDK 方法可以回滚。 (这也很奇怪。仍在弄清楚原因。)

如果我使用事务设置我的 SDK 方法,外部代码会因 TransactionAbortedException 崩溃:

System.Transactions.TransactionAbortedException : The transaction has aborted.
---- Npgsql.PostgresException : 55000: prepared transactions are disabled

目前我们在 SDK 的连接字符串中使用 enlist=false 来防止内部事务加入外部事务,但我想知道这种行为背后的原因。

这是我重现问题的代码

using (var scope = new TransactionScope(
TransactionScopeOption.required,new Transactionoptions
{
    IsolationLevel = IsolationLevel.ReadCommitted,},TransactionScopeAsyncFlowOption.Enabled))
{
    await using (var conn = new sqlConnection(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"))
    using (var cmd = new sqlCommand("insert into [Test].[dbo].[Test] (Id,\"Name\") values (1,'A')",conn))
    {
        await conn.OpenAsync();
        var result = await cmd.ExecuteNonQueryAsync();

        await SdkMethodTodoStuffWithNpgsql(1);

        scope.Complete();
    }
}

我使用 SdkMethodTodoStuffWithNpgsql() 来模拟存储库中注入 Postgres 上下文的方法

public async Task SdkMethodTodoStuffWithNpgsql(long id)
{
    var sqlScript = @"UPDATE test SET is_removal = TRUE WHERE is_removal = FALSE AND id = @id;
                    INSERT INTO log(id,data) SELECT id,data FROM log WHERE id = @id";

    using (var scope = new TransactionScope(
        TransactionScopeOption.RequiresNew,new Transactionoptions
        {
            IsolationLevel = IsolationLevel.ReadCommitted,TransactionScopeAsyncFlowOption.Enabled))
    {
        await using (var conn = new NpgsqlConnection(this._context.ConnectionString))
        {
            await conn.OpenAsync();
            using (var cmd = new NpgsqlCommand(sqlScript,conn))
            {

                cmd.Parameters.Add(new NpgsqlParameter("id",NpgsqlDbType.Bigint) { Value = id });
                await cmd.PrepareAsync();
                var result = await cmd.ExecuteNonQueryAsync();

                if (result != 2)
                {
                    throw new InvalidOperationException("Failed");
                }

                scope.Complete();
            }
        }
    }
}

解决方法

以上是预期的行为 - 在同一个 TransactionScope 中登记两个连接会触发“分布式事务”;这在 PostgreSQL 术语中称为“准备好的事务”,您必须在配置中启用它(这是您在上面看到的错误的原因)。如果打算有两个单独的事务(一个用于 SQL Server,一个用于 PostgreSQL)分别提交,那么选择退出登记是正确的做法。您还应该能够使用 TransactopScopeOption.Suppress。

请注意,.NET Core 目前不支持分布式事务,仅在 .NET Framework (see this issue) 中支持。因此,除非您使用 .NET Framework,否则即使您在 PostgreSQL 中启用准备好的事务,这也不起作用。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...