在 SQL 服务器中检测回滚

Detecting rollback in SQL Server

当涉及两个或多个语句时尝试检测回滚条件。对于 SqlCommand.ExecuteNonQuery 方法,docs 表示

If no statements are detected that contribute to the count, the return value is -1. If a rollback occurs, the return value is also -1.

我故意插入 -1 作为引用完整性约束的 table 中的无效条目。

string sql = $@"
    BEGIN TRANSACTION
        BEGIN TRY
            DELETE FROM CustomerContact WHERE CustomerId = @Id
            INSERT INTO CustomerContact(CustomerId, ContactId) VALUES (3, -1)
            COMMIT TRANSACTION
        END TRY
        BEGIN CATCH
            ROLLBACK TRANSACTION
        END CATCH";

using (SqlCommand command = new SqlCommand(sql, connection))
{
    command.Connection.Open();
    command.Parameters.Add("Id", SqlDbType.Int).Value = id;
    int result = command.ExecuteNonQuery();
    Console.WriteLine(result); // -> returns affected deleted rows but not -1
}

回滚按预期工作,但我没有从 ExecuteNonQuery 获得 -1,相反,我从 DELETE 操作获得了一些受影响的行(第一个语句)

我之前确实使用过 SqlTransaction,但我正在测试基于嵌入式 SQL 的交易的行为。

我建议您只放入 CATCH 块以指示错误和相应的回滚发生,而不是尝试检测 ROLLBACK 本身。

最好为 T-SQL 事务指定 SET XACT_ABORT ON;,以确保在超时的情况下立即回滚事务。这是因为超时发生在客户端,其中 API 取消了 运行 查询并阻止了带有 ROLLBACK 的 CATCH 块的执行。然后,连接与打开的事务一起返回到池中,锁尚未释放。虽然事务最终会在池连接 reused/reset 或关闭时回滚,但 XACT_ABORT 设置将导致立即发生并释放资源锁。

下面是我为大多数情况推荐的 T-SQL 事务管理和结构化错误处理模式。另外,请注意在 avoid surprises.

中自由使用分号
string sql = $@"
        SET XACT_ABORT ON;
        BEGIN TRY
            BEGIN TRANSACTION;
            DELETE FROM CustomerContact WHERE CustomerId = @Id;
            INSERT INTO CustomerContact(CustomerId, ContactId) VALUES (3, -1);
            COMMIT TRANSACTION;
        END TRY
        BEGIN CATCH
            IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
            THROW;
        END CATCH";

try
{
    using (SqlCommand command = new SqlCommand(sql, connection))
    {
        command.Connection.Open();
        command.Parameters.Add("Id", SqlDbType.Int).Value = id;
        int result = command.ExecuteNonQuery();
        Console.WriteLine(result); // -> returns affected deleted rows but not -1
    }
}
catch
{
    Console.WriteLine('handle exception here');
}