SqlException captura e manuseio

P: Existe uma maneira melhor de lidar com o SqlExceptions?

Os exemplos abaixo dependem da interpretação do texto na mensagem.

Por exemplo: 1 : Eu tenho uma captura existente para manipular se uma tabela não existir.
Ignore o fato de que eu poderia verificar se a tabela existe em primeiro lugar.

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

Eg2: sem o try catch mostrando exceção de chave duplicada

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

Solução: o início do meu 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); } } 

O SqlException tem uma propriedade Number que você pode verificar. Para erro duplicado, o número é 2601.

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

Para obter uma lista de todos os erros de SQL do seu servidor, tente isto:

  SELECT * FROM sysmessages 

Atualizar

Isso agora pode ser simplificado no C # 6.0

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

Mais ou menos. Consulte a causa e a resolução dos erros do mecanismo de database

 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: ... } } } 

O problema é que uma boa camada DAL nos faria TRY/CATCH dentro do T-SQL (stored procedures), com um padrão como tratamento de exceção e transactions aninhadas . Infelizmente um bloco TRY/CATCH T-SQL não pode elevar o código de erro original, terá que gerar um novo erro, com código acima de 50000. Isso torna o gerenciamento do lado do cliente um problema. Na próxima versão do SQL Server, há uma nova construção THROW que permite reaumentar a exceção original dos blocos catch T-SQL.

É melhor usar códigos de erro, você não precisa analisar.

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

Como descobrir que 208 deve ser usado:

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

Se você quiser lista de mensagens de erro atendidas no servidor SQL, você pode ver com

 SELECT * FROM master.dbo.sysmessages 

Com o MS SQL 2008, podemos listar mensagens de erro suportadas na tabela sys.messages

 SELECT * FROM sys.messages 

Se você está procurando uma maneira melhor de lidar com o SQLException, há algumas coisas que você pode fazer. Primeiro, o Spring.NET faz algo semelhante ao que você está procurando (eu acho). Aqui está um link para o que eles estão fazendo:

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

Além disso, em vez de olhar para a mensagem, você pode verificar o código de erro ( sqlEx.Number ). Essa parece ser a melhor maneira de identificar qual erro ocorreu. O único problema é que o número do erro retornado pode ser diferente para cada provedor de database. Se você planeja alternar provedores, você voltará a tratá-lo como está ou criar uma camada de abstração que traduza essas informações para você.

Aqui está um exemplo de um cara que usou o código de erro e um arquivo de configuração para traduzir e localizar mensagens de erro fáceis de usar:

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

Para aqueles de vocês novatos que podem lançar um erro de SQL ao se conectar ao database a partir de outra máquina (por exemplo, em load de formulário), você descobrirá que ao configurar pela primeira vez uma tabela de dados em C # que aponte para um database do SQL Server ele irá configurar uma conexão como esta:

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

Você pode precisar remover esta linha e substituí-la por algo como uma string de conexão tradicional, como mencionado no MSDN, etc.

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

Você pode avaliar com base no tipo de gravidade. Nota para usar isso, você deve estar inscrito em OnInfoMessage

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

Então seu OnInfoMessage conteria:

 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); }} 

Dessa forma, você pode avaliar a gravidade em vez do número do erro e ser mais eficaz. Você pode encontrar mais informações sobre gravidade aqui .

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

Eu estou trabalhando com código primeiro, C # 7 e estrutura de entidade 6.0.0.0. funciona para mim

 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: minha consulta para adicionar item ao database está em outro projeto (camada)