首页 > 解决方案 > 在 SQL Server 中检测回滚

问题描述

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

如果未检测到有助于计数的语句,则返回值为 -1。如果发生回滚,则返回值也是 -1。

-1故意将其作为无效条目插入具有参照完整性约束的被引用表中。

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
}

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

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

标签: c#sql-servertransactionsado.net

解决方案


我建议您只需在CATCH块中输入以指示错误并发生相应的回滚,而不是尝试检测ROLLBACK自身。

SET XACT_ABORT ON;使用 T-SQL 事务指定以确保在超时的情况下立即回滚事务是一种很好的做法。这是因为超时发生在客户端,API 取消正在运行的查询并阻止 CATCH 块的ROLLBACK执行。然后,连接将通过打开的事务返回到池中,并且锁尚未释放。尽管当池连接被重用/重置或关闭时事务最终会被回滚,但 XACT_ABORT 设置将导致立即发生并释放资源锁。

下面是我在大多数情况下推荐的 T-SQL 事务管理和结构化错误处理模式。另外,请注意自由使用分号以避免意外

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');
}

推荐阅读