2010-02-13 14 views
79

Desde una aplicación .NET 3.5/C#, me gustaría capturar SqlException pero solo si es causada por los interbloqueos en una instancia de SQL Server 2008.Cómo atrapar SqlException causado por interbloqueo?

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 documentado error code para esta excepción.

La excepción de filtrado en contra de 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?

+2

I (finalmente) encontré la documentación del código de error: http://msdn.microsoft.com/en-us/library/aa337376.aspx. También puede encontrar esto a través del mismo servidor SQL: 'select * from master.dbo.sysmessages donde error = 1205' –

Respuesta

127

El código de error específico de Microsft SQL Server para un punto muerto es 1205 por lo que necesitaría manejar la SqlException y verificar eso. Entonces, v.g. si por cualquier otro tipo de SqlException desea que la burbuja de la excepción arriba:

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

O, mediante el filtrado excepción disponible en C# 6

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

Una cosa útil que hacer para encontrar el código real de errores de SQL para un mensaje dado, es buscar en sys.messages en SQL Server.

p. Ej.

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

Una forma alternativa de manejar los bloqueos (de SQL Server 2005 y superior), es hacerlo dentro de un procedimiento almacenado utilizando el TRY ... CATCH apoyo:

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 here en MSDN de cómo implementar lógica de reintentos de interbloqueo puramente dentro de SQL.

+2

Tenga en cuenta que los códigos de error son específicos del proveedor, por lo que 1205 es un punto muerto para SQL Server, pero puede ser diferente para Oracle. MySQL, etc. – brianmearns

+3

Dependiendo de la capa de datos, la 'SqlException' puede ser envuelta en otra. Por lo tanto, es posible que necesitemos detectar cualquier tipo de excepción y verificarlos, si no son directamente una excepción de punto muerto, verifique recursivamente su 'InnerException'. –

36

Porque supongo que posiblemente desee detectar interbloqueos, para poder volver a intentar la operación fallida, me gustaría advertirle por un poco. Espero que me disculpes por estar un poco fuera de tema aquí.

Un punto muerto detectado por la base de datos revertirá efectivamente la transacción en la que se estaba ejecutando (si existe), 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 provocar daños en los datos.

Es importante tener esto en cuenta. Lo mejor es considerar la conexión completa condenada en caso de una falla causada por SQL. Volver a intentar la operación solo puede hacerse en el nivel donde se define la transacción (recreando esa transacción y su conexión).

Por lo tanto, cuando vuelva a intentar una operación fallida, asegúrese de abrir una conexión completamente nueva y comenzar una nueva transacción.

+4

¿Por qué necesita una conexión completamente nueva? He publicado una pregunta sobre esta respuesta [aquí] (http://stackoverflow.com/q/19108680/238753). – Sam

2

Aquí hay una forma C# 6 para detectar interbloqueos.

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 try..catch rodee toda su transacción. Según @Steven (ver su respuesta para más detalles), cuando falla el comando sql debido al interbloqueo, hace que la transacción se retrotraiga y, si no recrea la transacción, su reintento se ejecutará fuera del contexto de la transacción y puede provocar inconsistencias en los datos.

Cuestiones relacionadas