Intentando detectar una condición de reversión cuando hay dos o más declaraciones involucradas. Para el método SqlCommand.ExecuteNonQuery, docs dice

Si no se detectan declaraciones que contribuyan al recuento, el valor de retorno es -1. Si se produce una reversión, el valor de retorno también es -1.

Estoy insertando -1 a propósito como una entrada no válida en la tabla referenciada que tiene restricciones de integridad referencial.

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
}

La reversión funciona como se esperaba, pero no obtengo el -1 de ExecuteNonQuery, sino que obtengo una cantidad de filas afectadas de la operación DELETE ( la primera declaración )

Usé SqlTransaction antes, pero estoy probando el comportamiento de las transacciones basadas en SQL incorporado.

0
AppDeveloper 12 oct. 2019 a las 18:41

1 respuesta

La mejor respuesta

Le sugiero que agregue el bloque CATCH para indicar un error y la reversión correspondiente en lugar de intentar detectar el ROLLBACK en sí.

Es una buena práctica especificar SET XACT_ABORT ON; con transacciones T-SQL para garantizar que la transacción se deshaga inmediatamente en caso de que se agote el tiempo de espera. Esto se debe a que los tiempos de espera se producen en el lado del cliente, donde la API cancela la consulta en ejecución y evita que se ejecute el bloque CATCH con ROLLBACK. Luego, la conexión se devuelve al grupo con la transacción abierta y los bloqueos aún no se liberan. Aunque la transacción finalmente se revertirá cuando la conexión agrupada se reutilice / restablezca o se cierre, la configuración XACT_ABORT hará que eso ocurra inmediatamente y liberará los bloqueos de recursos.

A continuación se muestra la gestión de transacciones de T-SQL y el patrón de gestión de errores estructurados que recomiendo para la mayoría de las situaciones. Además, tenga en cuenta el uso liberal de punto y coma para evitar sorpresas. .

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');
}
1
Dan Guzman 12 oct. 2019 a las 16:38