Comment gérer db violations de contrainte dans l'interface utilisateur?
Nous mettre en œuvre la majorité de nos règles de gestion dans la base de données, stockées procs.
Je ne peux jamais décider de la meilleure façon de transmettre des données des erreurs de violation de contrainte à partir de la base de données à l'interface utilisateur. Les contraintes dont je parle sont liés plus des règles métier de l'intégrité des données.
Par exemple, un db erreur du type "Impossible d'insérer une ligne de clé en double" est la même que la règle d'entreprise "vous ne pouvez pas avoir plus d'un Foo avec le même nom". Mais nous avons "mis en œuvre" dans son sens commun lieu: comme une contrainte unique, qui lève une exception lorsque la règle est enfreinte.
D'autres règles telles que "Vous n'êtes autorisés à 100 Foos par jour" ne pas provoquer des erreurs par dire, car ils sont normalement traitées par code personnalisé comme return empty dataset
que le code de l'application vérifie et transmet retour à la couche d'interface utilisateur.
Et c'est là que le bât blesse. Notre code de l'interface utilisateur ressemble à ça (c'est AJAX.NET webservices code, mais n'importe quel framework ajax):
WebService.AddFoo("foo", onComplete, onError); // ajax call to web service
function onComplete(newFooId) {
if(!newFooId) {
alert('You reached your max number of Foos for the day')
return
}
// update ui as normal here
}
function onError(e) {
if(e.get_message().indexOf('duplicate key')) {
alert('A Foo with that name already exists');
return;
}
// REAL error handling code here
}
(Comme une note de côté: j'ai remarqué c'est ce que stackoverflow n'lorsque vous soumettez des commentaires trop rapidement: le serveur génère un HTTP 500
de réponse et de l'interface utilisateur l'attrape.)
Donc, vous voyez, nous sommes la manipulation des affaires de violation des règles dans les deux endroits, dont l'un (c'est à dire l'unique constaint d'erreur) est en train d'être traité comme un cas spécial pour le code qui est censé s'occuper de réel erreurs (pas des affaires de violations des règles), depuis .NET propage Exceptions tout le chemin jusqu'à la onError()
gestionnaire.
Cela se sent mal. Mes options, je pense que sont:
- attraper la "violation de clé en double' exception à l'application, au niveau du serveur et convertir ce que c'est l'interface utilisateur s'attend à ce que la "règle d'entreprise violé" drapeau,
- préempter l'erreur (par exemple, avec un
"select name from Foo where name = @Name"
) et retour quel qu'il soit, le serveur d'application s'attend à ce que la "règle d'entreprise violé" drapeau, - dans le même ordre de grandeur que les 2): tirer parti de la contrainte unique construit dans la base de la couche et aveuglément
insert into Foo
, attraper toutes les exceptions et les convertir en ce que le serveur d'application s'attend à ce que la "règle d'entreprise violé" drapeau - aveuglément
insert into Foo
(3) et de laisser cette Exception se propagent à l'interface utilisateur, plus l'application serveur de soulever des affaires de violations des règles comme de véritablesExceptions
(contre 1). De cette façon, TOUTES les erreurs sont traitées dans l'interface utilisateur de la coucheonError()
(ou similaire) du code.
Ce que j'aime à propos de 2) et 3), c'est que les affaires de violations des règles sont "jetés" où ils sont mis en œuvre: dans la procédure stockée. Ce que je n'aime pas à propos de 1) et 3) je pense ils impliquent stupide vérifie comme "if error.IndexOf('duplicate key')"
, tout comme ce qui est dans la couche d'interface utilisateur actuellement.
Modifier: j'aime 4), mais la plupart des gens disent utiliser Exception
s seulement dans exceptionnelle circonstances.
Alors, comment voulez-vous les gens à gérer la multiplication des affaires de violations des règles jusqu'à l'interface utilisateur élégante?
OriginalL'auteur Crescent Fresh | 2009-02-22
Vous devez vous connecter pour publier un commentaire.
Une procédure stockée peut utiliser l'instruction RAISERROR pour renvoyer des informations d'erreur à l'appelant. Ceci peut être utilisé d'une manière qui permet à l'utilisateur de l'interface de décider comment l'erreur s'affiche, tout en permettant à la procédure stockée à fournir les détails de l'erreur.
RAISERROR peut être appelée avec un msg_id, de la gravité et de l'état, et avec un ensemble d'erreur arguments. Lorsque utilisé de cette façon, un message avec le msg_id doivent avoir été saisies dans la base de données à l'aide de la procédure stockée système sp_addmessage. Cette msg_id peuvent être récupérées comme le ErrorNumber propriété dans la SqlException qui seront soulevées dans le .NET code d'appel de la procédure stockée. L'interface utilisateur peut alors décider sur quel type de message ou d'une autre indication de l'affichage.
L'erreur arguments sont substitués dans la message d'erreur de la même manière que le printf déclaration de travaux en C. Cependant, si vous voulez juste passer les arguments de retour à l'INTERFACE utilisateur de sorte que l'INTERFACE utilisateur peut décider de la façon de les utiliser, il suffit de faire les messages d'erreur ont pas de texte, juste des espaces réservés pour les arguments. Un message peut être " "%s"|%d " pour passer à un argument chaîne de caractères (entre guillemets), et un argument numérique. L' .NET code pourrait scinder en dehors et de les utiliser dans l'interface utilisateur mais que vous aimez.
RAISERROR peut également être utilisé dans un bloc TRY CATCH dans la procédure stockée. Qui vous permettra d'attraper le double de la clé d'erreur, et le remplacer par votre propre numéro d'erreur qui signifie "clé en double lors de l'insertion" de votre code, et il peut inclure la valeur de clé(s). Votre INTERFACE utilisateur peut l'utiliser pour afficher "numéro de Commande existe déjà", où "x" est la valeur de la clé fournie.
Le code qui appelle éventuellement
RAISERROR
aurez toujours besoin de faire un "stupide cochez la case" commeCHARINDEX('duplicate key', @errorMessage) > 0
et j'ai trouvé cette question parce que mon exemple de scénario implique deux potentiel de la clé unique de violations de contrainte et j'étais curieux de savoir si il y avait moyen de déterminer quelle touche a été violé sans la recherche pour les noms des touches, ou les noms des colonnes pertinentes.OriginalL'auteur John Saunders
Nous ne réalisons pas notre logique métier dans la base de données, mais nous avons tous nos validation côté serveur, avec un faible niveau de DB opérations CRUD séparés de la hausse du niveau de la logique métier et le code du contrôleur.
Ce que nous essayons de faire à l'interne est de faire passer une validation de l'objet avec des fonctions comme
Validation.addError(message,[fieldname])
. Les différentes couches applicatives ajouter de validation des résultats sur cet objet et ensuite, nous appelonsValidation.toJson()
afin d'obtenir un résultat qui ressemble à ceci:Cela peut être facilement traitées côté client pour afficher les messages liés à des champs individuels ainsi que les messages d'ordre général.
Concernant les violations de contrainte que nous utilisons #2, c'est à dire vérifier les violations potentielles avant d'insérer/mettre à jour et ajouter de l'erreur à la validation de l'objet.
J'aime cette approche, et il ne serait pas impossible de faire de même avec les entreprises de la règle vérifie tous pris en compte dans la db. Le serveur d'application juste de traduire toutes les réponses à une norme de l'interface utilisateur peut gérer. Ce qui n'est pas une mauvaise chose à faire, peu importe.
OriginalL'auteur Mark Porter
Le problème est vraiment une limitation dans l'architecture de votre système. En poussant la logique à la base de données, vous avez besoin de le manipuler en deux endroits (par opposition à la construction d'une couche de logique métier qui relie l'INTERFACE avec la base de données. Puis de nouveau, à la minute que vous avez une couche de logique métier, vous perdez tous les avantages d'avoir une logique dans stockées procs. Ne préconise pas l'un ou l'autre. Les deux sucer aussi. Ou ne pas le sucer. Selon la façon dont vous le regardez.
Où en étais-je?
La droite.
Je pense qu'une combinaison de 2 et 3 est probablement la voie à suivre.
Par préjuger de l'erreur, vous pouvez créer un ensemble de procédures qui peuvent être appelées à partir de l'INTERFACE utilisateur en face de code afin de fournir des détails spécifiques à l'implémentation de la rétroaction à l'utilisateur. Vous n'avez pas nécessairement besoin de le faire avec ajax sur un champ par champ, mais vous pourriez.
Les contraintes et les autres règles qui sont à la base de données devient alors le final de la santé mentale-vérifier toutes les données, et ne peut supposer que les données sont de bonne qualité avant d'être envoyé, et de lancer des Exceptions, comme une question de cours (l'idée étant que ces procédures doivent toujours être appelé avec des données valides et à cet effet, des données non valides est une circonstance Exceptionnelle).
Vous avez raison ...
OriginalL'auteur Toby Hede
Dans la défense de #4, SQL Server a une jolie ordonnée de la hiérarchie de l'erreur de la gravité des niveaux prédéfinis. Car comme vous le soulignez, c'est bien pour gérer les erreurs, dont la logique est, je serais enclin à gérer ce par convention entre le PS et l'abstraction de l'INTERFACE utilisateur, plutôt que d'ajouter un tas de supplémentaires de couplage. En particulier depuis, vous pouvez déclencher des erreurs avec à la fois une valeur et d'une chaîne.
OriginalL'auteur dkretz
J'ai vu beaucoup de Ajax applications de base de faire un contrôle en temps réel sur des champs tels que le nom d'utilisateur (pour voir si il existe déjà) dès que l'utilisateur quitte la zone d'édition. Il me semble qu'une meilleure approche que de les laisser à la base de données de soulever une exception fondée sur un db de la contrainte, il est plus actif puisque vous avez un réel processus: obtenir la valeur, vérifier pour voir si elle est valide, afficher le message d'erreur si non, laissez-le continuer si pas d'erreur. Il semble donc que l'option 2 est un bon.
Nous arrivons dans la condition de la course en faisant un plein de validation côté serveur sur soumettre. La validation de champ est juste une commodité pour l'utilisateur
pour sûr. Je me demande comment les gens à gérer le "plein la validation côté serveur" dans l'interface utilisateur (en supposant que l'ajax pour envoyer les données) lorsqu'une violation t se produire - ce qui vous a répondu dans ton autre réponse, je vous remercie.
OriginalL'auteur Otávio Décio
C'est la façon dont je fais les choses, si elle ne peut pas être le meilleur pour vous:
Je vont généralement pour la préemption de modèle, même si cela dépend beaucoup de votre architecture de l'application.
Pour moi (dans mon environnement), il est logique de vérifier pour la plupart des erreurs dans le milieu (business objects) de niveau. C'est là où toutes les autres affaires spécifiques de logique a lieu, donc j'essaie de garder autant du reste de ma logique, ici aussi. Je pense que la base de données comme un endroit pour enregistrer mes objets.
Quand il s'agit de la validation, la méthode la plus simple des erreurs peuvent être piégés dans le javascript (mise en forme, les longueurs de champ, etc.), bien sûr, vous ne supposez jamais que ces contrôles d'erreur a eu lieu. Ces erreurs également vérifiée dans le plus sûr, de plus en plus contrôlés du monde de code côté serveur.
Des règles de gestion (tels que "vous ne pouvez avoir de nombreuses foos par jour") est contrôlée dans le code côté serveur, dans l'entreprise objet de la couche.
Seulement les règles de données vérifiées dans la base de données (intégrité référentielle, unique champ de contraintes, etc.). Nous devancer la vérification de l'ensemble de ces le niveau intermédiaire trop, pour éviter de heurter la base de données inutilement.
Donc ma base de données ne se protège contre le simple, centrée sur les données, les règles qu'il est bien équipé pour faire face; la plus variable, axé sur les affaires des règles de vivre en terre d'objets, plutôt que de la terre de dossiers.
Ouais, c'est totalement de la faiblesse de cette approche. Dans mon environnement, c'est un cas rare que nous n'avons pas besoin de s'inquiéter à ce sujet. Il en résulterait une assez moche exception, c'est vrai, mais la base reste intacte.
OriginalL'auteur teedyay