Cómo capturar SQLException causado por un bloqueo?


Desde una aplicación.NET 3.5 / C#, me gustaría capturar SqlException pero solo si es causada por bloqueos en una instancia de SQL Server 2008.

El mensaje de error típico es Transaction (Process ID 58) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Sin embargo, no parece ser un código de error documentado para esta excepción.

Filtrar la excepción contra la presencia de la palabra clave deadlock en su mensaje parece una forma muy fea de lograr este comportamiento. ¿Alguien sabe la forma correcta de hacer esto?

Author: AdaTheDev, 2010-02-13

3 answers

El código de error específico de Microsft SQL Server para un bloqueo es 1205, por lo que debe manejar la excepción SQLException y verificarlo. Así, por ejemplo, si para todos los otros tipos de SQLException desea que la burbuja de la excepción:

catch (SqlException ex)
{
    if (ex.Number == 1205)
    {
        // Deadlock 
    }
    else
        throw;
}

O, usando el filtrado de excepciones disponible en C# 6

catch (SqlException ex) when (ex.Number == 1205)
{
    // Deadlock 
}

Una cosa útil para encontrar el código de error SQL real para un mensaje dado, es buscar en sys.mensajes en SQL Server.

Por ejemplo

SELECT * FROM sys.messages WHERE text LIKE '%deadlock%' AND language_id=1033

Una forma alternativa de manejar los bloqueos (de SQL Server 2005 y superiores), es hacerlo dentro de un procedimiento almacenado usando el TRY...Soporte de captura:

BEGIN TRY
    -- some sql statements
END TRY
BEGIN CATCH
    IF (ERROR_NUMBER() = 1205)
        -- is a deadlock
    ELSE
        -- is not a deadlock
END CATCH

Hay un ejemplo completo aquí en MSDN de cómo implementar la lógica de reintento de punto muerto puramente dentro de SQL.

 136
Author: AdaTheDev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-14 15:00:53

Porque supongo que es posible que desee detectar puntos muertos, para poder volver a intentar la operación fallida, me gusta advertirle para un poco gotcha. Espero que me disculpen por estar un poco fuera de tema.

Un bloqueo detectado por la base de datos revertirá efectivamente la transacción en la que se estaba ejecutando (si la hubiera), mientras que la conexión se mantiene abierta en. NET. Volver a intentar esa operación (en esa misma conexión), significa que se ejecutará en un contexto sin transacción y esto podría conducir a corrupción de datos.

Es importante ser consciente de esto. Es mejor considerar la conexión completa condenada en caso de un fallo causado por SQL. Volver a intentar la operación solo se puede hacer en el nivel donde se define la transacción (recreando esa transacción y su conexión).

Así que cuando vuelva a intentar una operación fallida, asegúrese de abrir una conexión completamente nueva e iniciar una nueva transacción.

 39
Author: Steven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-14 14:39:53

Aquí hay una forma de C# 6 de detectar puntos muertos.

try
{
    //todo: Execute SQL. 
    //IMPORTANT, if you used Connection.BeginTransaction(), this try..catch must surround that code. You must rollback the original transaction, then recreate it and re-run all the code.
}
catch (SqlException ex) when (ex.Number == 1205)
{
    //todo: Retry SQL
}

Asegúrese de que este intento..catch rodea toda su transacción. Según @ Steven (vea su respuesta para más detalles), cuando el comando sql falla debido al bloqueo, hace que la transacción se revierta y, si no vuelve a crear la transacción, su reintento se ejecutará fuera del contexto de la transacción y puede resultar en inconsistencias de datos.

 2
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-21 22:25:01