2010-01-17 2 views
2

J'ai une simple table de pays utilisant une colonne d'identité pour la clé primaire. Il y a également des colonnes utilisées pour contenir les codes de pays ISO-3166 à 2 lettres et 3 lettres. Chacune de ces colonnes est définie comme un index unique.T-SQL Reporting Duplicate Key Exception à l'utilisateur de manière simple

Sur insertions/mises à jour Je souhaite simplement informer l'utilisateur si le code ISO entré est déjà utilisé. Je compte sur l'exception de base de données pour déclencher le processus. Je ne veux pas submerger les utilisateurs avec les détails techniques. Je veux juste lui dire que la valeur ISO entrée ne peut pas être utilisée.

Voici le T-SQL dans le Procès stocké que j'ai écrit. Il semble long et sujet à de futurs bugs et/ou à une maintenance continue au fur et à mesure que l'application se développe et que les choses changent. Y a-t-il un moyen meilleur ou plus simple? Je pourrais être un peu mouillé derrière les oreilles avec .net, mais je pensais que ce serait plus facile.

BEGIN TRY 
    UPDATE [Country] SET [CountryName] = @CountryName, [CountryISO] = @CountryISO, [CountryISO3] = @CountryISO3, [UpdateDate] = @updateDate WHERE (([CountryID] = @CountryID) AND ([RowVersion] = @Original_RowVersion)); 
END TRY 

BEGIN CATCH 
    DECLARE @ErrSeverity int, @ErrNumber int, @ErrLine int 
    DECLARE @ErrMsg nvarchar(4000) 
    SELECT @ErrSeverity = ERROR_SEVERITY(), @ErrNumber = ERROR_NUMBER(),@ErrState = ERROR_STATE(), 
     @ErrMsg = 
      CASE WHEN ERROR_NUMBER() = 2601 
      THEN 
       CASE 
       WHEN ISNULL(CHARINDEX('IX_COUNTRYISO3', ERROR_MESSAGE()), 0) > 0 
       THEN 'The 3 letter ISO-3166 value entered is already in use. Please enter a unique 3 letter ISO-3166 value.' 
       WHEN ISNULL(CHARINDEX('IX_COUNTRYISO', ERROR_MESSAGE()), 0) > 0 
       THEN 'The 2 letter ISO-3166 value entered is already in use. Please enter a unique 2 letter ISO-3166 value.' 
       ELSE 
       ERROR_MESSAGE() + '(SQL ErrNo: ' + CONVERT(varchar(50), ERROR_NUMBER()) + ')' 
       END 
      ELSE 
       ERROR_MESSAGE() + '(SQL ErrNo: ' + CONVERT(varchar(50), ERROR_NUMBER()) + ')' 
      END; 
    RAISERROR(@ErrMsg, @ErrSeverity, @ErrState) 
END CATCH 

Ma solution est-elle viable?
Quelle est la meilleure façon de partager ce code d'exception entre l'insertion et la mise à jour StoredProcs?

Je suppose que je demande essentiellement une révision de code.

Merci beaucoup Mike

Répondre

1

Je suis d'accord, le code comme ça va devenir un casse-tête d'entretien. Il n'y a rien de mal avec le code en soi, c'est juste que le problème est pris trop tard.

Les contraintes/index uniques sont une «dernière ligne de défense», du moins dans mon monde. Si vous souhaitez offrir une bonne expérience utilisateur, vous ne pouvez pas attendre que les données soient déjà soumises à la base de données. Vous devez vérifier activement les doublons au niveau de l'interface utilisateur et avertir l'utilisateur qu'il est sur le point de soumettre une entrée en double (et désactiver la soumission si les clés en double ne sont pas autorisées).

Si une clé dupliquée finit par être soumise, cela décrit essentiellement un bogue dans la logique de l'application, donc il est normal que votre base de données crache un message d'erreur "base de données", vous n'avez pas besoin d'essayer pour le dire. L'application doit essayer de gérer ces erreurs aussi élégamment que possible, comme avec toute autre exception inattendue.

Si vous demandez simplement s'il existe un moyen de modulariser ce type de fonctionnalité dans T-SQL ... Je ne pense pas. Pas sans un désordre impie de SQL dynamique, de toute façon.

Mise à jour - juste vu un commentaire à l'autre réponse, si vous voulez obtenir le numéro d'erreur SQL retour en C# alors il est assez simple:

const int SqlDuplicateKeyError = 2601; 

try 
{ 
    db.Update(record); 
} 
catch (SqlException ex) 
{ 
    switch (ex.Number) 
    { 
     case SqlDuplicateKeyError: 
      // Custom error handling here 
     default: 
      throw; 
    } 
} 
+0

@Aaronaught: La vérification des valeurs par rapport à la base de données avant la soumission semble être une bonne stratégie. Peut-être ajouterai-je un appel AJAX lorsque les valeurs des deux zones de texte changent. Cela me permettra également d'utiliser une stratégie plus générique pour afficher et collecter des informations d'exception. Merci pour vos commentaires. Je posterai avec tout progrès que je fais. Cela peut ne pas être pour plusieurs jours cependant. – MikeSullivan

+0

Dans un environnement multi-utilisateur, vous ne pouvez pas compter sur des contrôles en double au niveau de l'interface utilisateur. Les réglages de l'interface utilisateur devraient être là, mais le traitement de l'erreur de la clé en double devrait l'être aussi. Maintenant, puisque l'ajustement d'index est une tâche courante pour un DBA, laisser le client analyser le message d'erreur est une mauvaise idée. Généralement, c'est une très mauvaise idée de montrer des erreurs SQL à l'utilisateur. Je pense vraiment que la solution de MikeSullivan est viable. – l33t

+0

@ l33t: Ceci n'analyse pas l'erreur * message *, il regarde l'erreur * code *. C'est ce que les codes d'erreur sont * pour *. Et comme c'est une mauvaise idée de montrer des erreurs SQL à l'utilisateur, c'est une mauvaise idée de montrer * toutes les erreurs spécifiques à l'utilisateur. La plupart des applications modernes utilisent des opérations fire-and-forget avec une cohérence éventuelle, de sorte que les erreurs sont un problème de développeur plutôt que d'utilisateur et lorsqu'elles doivent montrer une erreur interne, elles le font avec une erreur générique. écran et quelques étapes de dépannage, comme "actualiser la page et réessayer votre dernière action". – Aaronaught

0

Personnellement, j'imagine que le simple fait de donner à vos contraintes unqique/index des noms sensibles et verbeux serait une meilleure option que cela.

Si les utilisateurs interagissent directement avec la base de données, ils doivent au moins avoir une connaissance approfondie des technologies de l'information.

Si les utilisateurs interagissent via un frontal personnalisé, vous devez implémenter la gestion des erreurs dans le frontal.

Pour l'attraper à l'avant, voir ce link, un exemple de base (et l'erreur probable monté) exemple ...

Catch (SqlExpcetion ex) 
{ 
    if (e.Number == 2061 && ex.Message.Contains("IX_COUNTRYISO3") 
    { 
     MessageBox.Show("The 3 letter ISO-3166 value entered is already in use. Please enter a unique 3 letter ISO-3166 value."); 
    } 
    else 
    { 
     MessageBox.Show(String.Format("Error ({0}):{1}",ex.Number,ex.Message) 
    } 
} 
Catch (Exception ex) 
{ 
    MessageBox.Show(ex); 
} 
+0

@ Paul: Merci pour vos commentaires. Je ne sais pas si je comprends ce que vous voulez dire par des noms verbeux. Cela signifie-t-il que je devrais donner à ces contraintes uniques de meilleurs noms dans la base de données? Les utilisateurs interagissent via un frontal personnalisé. Je ne pouvais pas comprendre comment le numéro d'erreur SQL à travers la classe d'exception. Je suppose que je pourrais juste rechercher la chaîne de message pour «clé en double». Je dois approfondir ma compréhension des classes d'exception .net. Merci – MikeSullivan

+0

édité avec un bref exemple d'attraper une exception sql. –

+0

Je suis d'accord avec Aaronaught cependant, vous ne devriez pas permettre à un utilisateur de même essayer ceci, la mise à jour devrait être validée avant qu'elle ne soit exécutée. –

Questions connexes