简体   繁体   中英

Detecting rollback in SQL Server

Trying to detect a rollback condition when there are two or more statements involved. For SqlCommand.ExecuteNonQuery Method, The docs says

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.

I'm inserting -1 on purpose as an invalid entry in the referenced table having referential integrity constraints.

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
}

Rollback is working as expected but I'm not getting the -1 from ExecuteNonQuery , instead, I get a number of affected rows from the DELETE operation ( the first statement )

I did use SqlTransaction earlier but I'm testing out the behavior of embedded SQL based transactions.

I suggest you just throw in the CATCH block to indicate an error and corresponding rollback occurred rather than try to detect the ROLLBACK itself.

It's a good practice to specify SET XACT_ABORT ON; with T-SQL transactions to ensure the transaction is rolled back immediately in the case of a timeout. This is because timeouts occur on the client side, where the API cancels the running query and prevents the CATCH block with the ROLLBACK from executing. The connection then gets returned to the pool with the open transaction and the locks are not yet released. Although the transaction will be eventually get rolled back when the pooled connection is reused/reset or closed, the XACT_ABORT setting will cause that to occur immediately and release resource locks.

Below is the T-SQL transaction management and structured error handling pattern I recommend for most situations. Also, note the liberal use of semi-colons to 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');
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM