在 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');
}
当涉及两个或多个语句时尝试检测回滚条件。对于 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');
}