Captura y manejo de SqlException

P: ¿Hay una mejor manera de manejar SqlExceptions?

Los ejemplos a continuación se basan en la interpretación del texto en el mensaje.

Eg1: Tengo una trampa de prueba existente para manejar si una tabla no existe.
Ignore el hecho de que podría verificar si la tabla existe en primer lugar.

try { //code } catch(SqlException sqlEx) { if (sqlEx.Message.StartsWith("Invalid object name")) { //code } else throw; } 

Eg2: sin la captura de prueba que muestra la excepción de clave duplicada

 if (sqlEx.Message.StartsWith("Cannot insert duplicate key row in object")) 

Solución: el inicio de mi SqlExceptionHelper

 //-- to see list of error messages: select * from sys.messages where language_id = 1033 order by message_id public static class SqlExceptionHelper { //-- rule: Add error messages in numeric order and prefix the number above the method //-- 208: Invalid object name '%.*ls'. public static bool IsInvalidObjectName(SqlException sex) { return (sex.Number == 208); } //-- 2601: Cannot insert duplicate key row in object '%.*ls' with unique index '%.*ls'. The duplicate key value is %ls. public static bool IsDuplicateKey(SqlException sex) { return (sex.Number == 2601); } } 

La SqlException tiene una propiedad Number que puedes verificar. Para error duplicado, el número es 2601.

 catch (SqlException e) { switch (e.Number) { case 2601: // Do something. break; default: throw; } } 

Para obtener una lista de todos los errores SQL de su servidor, intente esto:

  SELECT * FROM sysmessages 

Actualizar

Esto ahora se puede simplificar en C # 6.0

 catch (SqlException e) when (e.Number == 2601) { // Do something. } 

Más o menos, tipo de. Consulte Causa y resolución de errores del motor de base de datos

 class SqllErrorNumbers { public const int BadObject = 208; public const int DupKey = 2627; } try { ... } catch(SqlException sex) { foreach(SqlErrorCode err in sex.Errors) { switch (err.Number) { case SqlErrorNumber.BadObject:... case SqllErrorNumbers.DupKey: ... } } } 

Sin embargo, el problema es que una buena capa de DAL nos haría TRY/CATCH dentro de T-SQL (procedimientos almacenados), con un patrón como el manejo de excepciones y las transacciones anidadas . Por desgracia, un bloque TRY/CATCH T-SQL no puede generar el código de error original, tendrá que generar un nuevo error, con un código por encima de 50000. Esto hace que el manejo del lado del cliente sea un problema. En la próxima versión de SQL Server, hay una nueva construcción THROW que permite volver a generar la excepción original de los bloques catch T-SQL.

Es mejor usar códigos de error, no es necesario analizarlos.

 try { } catch (SqlException exception) { if (exception.Number == 208) { } else throw; } 

Cómo saber que 208 debe usarse:

 select message_id from sys.messages where text like 'Invalid object name%' 

Si desea que la lista de mensajes de error se encuentre en el servidor Sql, puede ver con

 SELECT * FROM master.dbo.sysmessages 

Con MS SQL 2008, podemos enumerar los mensajes de error admitidos en la tabla sys.messages

 SELECT * FROM sys.messages 

Si está buscando una forma mejor de manejar SQLException, hay algunas cosas que puede hacer. Primero, Spring.NET hace algo similar a lo que está buscando (creo). Aquí hay un enlace a lo que están haciendo:

http://springframework.net/docs/1.2.0/reference/html/dao.html

Además, en lugar de mirar el mensaje, puede verificar el código de error ( sqlEx.Number ). Esa parece ser una mejor forma de identificar qué error ocurrió. El único problema es que el número de error devuelto puede ser diferente para cada proveedor de base de datos. Si planea cambiar de proveedor, volverá a manejarlo tal como está o a crear una capa de abstracción que traduzca esta información por usted.

Aquí hay un ejemplo de un chico que usó el código de error y un archivo de configuración para traducir y localizar mensajes de error fáciles de usar:

https://web.archive.org/web/20130731181042/http://weblogs.asp.net/guys/archive/2005/05/20/408142.aspx

Para aquellos de ustedes novatos que pueden arrojar un error de SQL al conectarse a la base de datos desde otra máquina (por ejemplo, en la carga de formulario), encontrarán que cuando configuran por primera vez una tabla de datos en C # que apunta a una base de datos SQL Server configurará una conexión como esta:

 this.Table_nameTableAdapter.Fill(this.DatabaseNameDataSet.Table_name); 

Es posible que deba eliminar esta línea y reemplazarla por otra cosa, como una cadena de conexión tradicional como se menciona en MSDN, etc.

http://www.connectionstrings.com/sql-server-2008

Puede evaluar según el tipo de gravedad. Nota para usar esto debe estar suscrito a OnInfoMessage

 conn.InfoMessage += OnInfoMessage; conn.FireInfoMessageEventOnUserErrors = true; 

Entonces su OnInfoMessage contendría:

 foreach(SqlError err in e.Errors) { //Informational Errors if (Between(Convert.ToInt16(err.Class), 0, 10, true)) { logger.Info(err.Message); //Errors users can correct. } else if (Between(Convert.ToInt16(err.Class), 11, 16, true)) { logger.Error(err.Message); //Errors SysAdmin can correct. } else if (Between(Convert.ToInt16(err.Class), 17, 19, true)) { logger.Error(err.Message); //Fatal Errors 20+ } else { logger.Fatal(err.Message); }} 

De esta forma puede evaluar la gravedad en lugar del número de error y ser más eficaz. Puede encontrar más información sobre la gravedad aquí .

 private static bool Between( int num, int lower, int upper, bool inclusive = false ) { return inclusive ? lower <= num && num <= upper : lower < num && num < upper; } 

Estoy trabajando con el código primero, C # 7 y el marco de la entidad 6.0.0.0. esto funciona para mi

 Add() { bool isDuplicate = false; try { //add to database } catch (DbUpdateException ex) { if (dbUpdateException.InnerException != null) { var sqlException = dbUpdateException.InnerException.InnerException as SqlException; if(sqlException == null) isDuplicate = IsDuplicate(sqlException); } } catch (SqlException ex) { isDuplicate = IsDuplicate(ex); } if(isDuplicate){ //handle here } } bool IsDuplicate(SqlException sqlException) { switch (sqlException.Number) { case 2627: return true; default: return false; } } 

NB: mi consulta para agregar elemento a db está en otro proyecto (capa)