Q: Existe-t-il une meilleure façon de gérer les exceptions SQLLe?
Les exemples ci-dessous reposent sur l'interprétation du texte du message.
Eg1: J'ai un catch existant à manipuler si un tableau n'existe pas.
Ignore le fait que je puisse vérifier si la table existe en premier lieu.
try
{
//code
}
catch(SqlException sqlEx)
{
if (sqlEx.Message.StartsWith("Invalid object name"))
{
//code
}
else
throw;
}
Eg2: sans le catch try montrant une exception de clé dupliquée
if (sqlEx.Message.StartsWith("Cannot insert duplicate key row in object"))
Solution: Le début de mon 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); }
}
SqlException a une propriété Number que vous pouvez vérifier. Pour les erreurs en double, le nombre est 2601.
catch (SqlException e)
{
switch (e.Number)
{
case 2601:
// Do something.
break;
default:
throw;
}
}
Pour obtenir une liste de toutes les erreurs SQL de votre serveur, essayez ceci:
SELECT * FROM sysmessages
Mettre à jour
Ceci peut maintenant être simplifié en C # 6.0
catch (SqlException e) when (e.Number == 2601)
{
// Do something.
}
En quelque sorte. Voir Causes et résolution des erreurs du moteur de base de données
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: ...
}
}
}
Le problème cependant est qu’une bonne couche DAL utiliserait TRY/CATCH
dans le T-SQL (procédures stockées), avec un modèle du type Traitement des exceptions et transactions imbriquées . Hélas, un bloc T-SQL TRY/CATCH
ne peut pas générer le code d'erreur d'origine, mais devra générer une erreur new, avec un code supérieur à 50000. Cela rend le traitement du côté client impossible. Dans la prochaine version de SQL Server, il existe une nouvelle construction THROW qui permet de recréer l'exception d'origine à partir des blocs de capture T-SQL.
Il est préférable d'utiliser des codes d'erreur, vous n'avez pas à analyser.
try
{
}
catch (SqlException exception)
{
if (exception.Number == 208)
{
}
else
throw;
}
Comment savoir que 208 doit être utilisé:
select message_id
from sys.messages
where text like 'Invalid object name%'
Si vous voulez une liste des messages d’erreur rencontrés sur le serveur SQL, vous pouvez voir avec
SELECT *
FROM master.dbo.sysmessages
Si vous cherchez une meilleure façon de gérer SQLException, vous pouvez faire certaines choses. Premièrement, Spring.NET fait quelque chose de similaire à ce que vous recherchez (je pense). Voici un lien vers ce qu'ils font:
http://springframework.net/docs/1.2.0/reference/html/dao.html
De plus, au lieu de regarder le message, vous pouvez vérifier le code d'erreur (sqlEx.Number
). Cela semblerait être un meilleur moyen d’identifier quelle erreur s’est produite. Le seul problème est que le numéro d'erreur renvoyé peut être différent pour chaque fournisseur de base de données. Si vous envisagez de changer de fournisseur, vous serez amené à le gérer comme vous êtes ou à créer une couche d'abstraction traduisant ces informations pour vous.
Voici un exemple d'un gars qui a utilisé le code d'erreur et un fichier de configuration pour traduire et localiser des messages d'erreur conviviaux:
Avec MS SQL 2008, nous pouvons répertorier les messages d'erreur pris en charge dans la table sys.messages.
SELECT * FROM sys.messages
Pour ceux d’entre vous qui sont des recrues et qui risquent de générer une erreur SQL lors de la connexion à la base de données depuis une autre machine (par exemple, lors du chargement du formulaire), vous constaterez que lors de la première installation d’un fichier datatable en C # qui pointe il va configurer une connexion comme celle-ci:
this.Table_nameTableAdapter.Fill(this.DatabaseNameDataSet.Table_name);
Vous devrez peut-être supprimer cette ligne et la remplacer par quelque chose d'autre, comme une chaîne de connexion traditionnelle, comme mentionné sur MSDN, etc.
Je travaille d'abord avec le code, C # 7 et l'entité framework 6.0.0.0. ça marche pour moi
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;
}
}
N.B: ma requête pour ajouter un élément à la base de données est dans un autre projet (couche)
Vous pouvez évaluer based sur le type de gravité. Remarque pour utiliser cela, vous devez être abonné à OnInfoMessage
conn.InfoMessage += OnInfoMessage;
conn.FireInfoMessageEventOnUserErrors = true;
Ensuite, votre OnInfoMessage contiendrait:
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 cette façon, vous pouvez évaluer la gravité plutôt que le numéro d'erreur et être plus efficace. Vous pouvez trouver plus d'informations sur la gravité ici .
private static bool Between( int num, int lower, int upper, bool inclusive = false )
{
return inclusive
? lower <= num && num <= upper
: lower < num && num < upper;
}