web-dev-qa-db-fra.com

SqlException capture et manipulation

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

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

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/CATCHdans 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.

20
Remus Rusanu

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%'
7
Alex Aza

Si vous voulez une liste des messages d’erreur rencontrés sur le serveur SQL, vous pouvez voir avec

SELECT *
FROM master.dbo.sysmessages
4
Snake Eyes

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:

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

1
IAmTimCorey

Avec MS SQL 2008, nous pouvons répertorier les messages d'erreur pris en charge dans la table sys.messages.

SELECT * FROM sys.messages
1
Thibaut Brard

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. 

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

0
Chris

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)

0
reza.cse08

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;
}
0
Nim