Ayant deux classes:
public class Parent
{
public int Id { get; set; }
public int ChildId { get; set; }
}
public class Child { ... }
Lors de l'affectation ChildId
à, Parent
dois-je d'abord vérifier s'il existe dans la base de données ou attendre que la base de données lève une exception?
Par exemple (en utilisant Entity Framework Core):
REMARQUE: ces types de vérifications sont TOUS SUR INTERNET, même sur les documents officiels de Microsoft: https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using- mvc / traitement-concurrence-avec-l'entité-structure-dans-un-asp-net-mvc-application # modifier-le-contrôleur-département, mais il existe une gestion supplémentaire des exceptions pourSaveChanges
Notez également que l'objectif principal de cette vérification était de renvoyer un message convivial et un statut HTTP connu à l'utilisateur de l'API et de ne pas ignorer complètement les exceptions de base de données. Et la seule exception à être levée est à l'intérieur SaveChanges
ou SaveChangesAsync
appeler ... donc il n'y aura pas d'exception lorsque vous appelez FindAsync
ou Any
. Donc, si l'enfant existe mais a été supprimé avant, SaveChangesAsync
une exception d'accès simultané sera levée.
J'ai fait cela parce que l' foreign key violation
exception sera beaucoup plus difficile à formater pour afficher "L'enfant avec l'id {parent.ChildId} est introuvable."
public async Task<ActionResult<Parent>> CreateParent(Parent parent)
{
// is this code redundant?
// NOTE: its probably better to use Any isntead of FindAsync because FindAsync selects *, and Any selects 1
var child = await _db.Children.FindAsync(parent.ChildId);
if (child == null)
return NotFound($"Child with id {parent.ChildId} could not be found.");
_db.Parents.Add(parent);
await _db.SaveChangesAsync();
return parent;
}
contre:
public async Task<ActionResult<Parent>> CreateParent(Parent parent)
{
_db.Parents.Add(parent);
await _db.SaveChangesAsync(); // handle exception somewhere globally when child with the specified id doesn't exist...
return parent;
}
Le deuxième exemple dans Postgres va générer une 23503 foreign_key_violation
erreur: https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
L'inconvénient de la gestion des exceptions de cette manière dans ORM comme EF est que cela ne fonctionnera qu'avec un système de base de données spécifique. Si vous avez toujours voulu passer au serveur SQL ou à quelque chose d'autre, cela ne fonctionnera plus car le code d'erreur sera modifié.
Ne pas formater correctement l'exception pour l'utilisateur final pourrait exposer des choses que vous ne voulez que les développeurs voient.
En relation:
https://stackoverflow.com/questions/308905/should-there-be-a-transaction-for-read-queries
Child with id {parent.ChildId} could not be found.
. Et le formatage "violation de clé étrangère" est selon moi pire dans ce cas.Réponses:
Plutôt une question confuse, mais OUI vous devriez vérifier d'abord et ne pas manipuler simplement une exception de base de données.
Tout d’abord, dans votre exemple, vous vous trouvez au niveau de la couche de données, en utilisant EF directement sur la base de données pour exécuter SQL. Votre code est équivalent à courir
L'alternative que vous suggérez est:
L'utilisation de l'exception pour exécuter une logique conditionnelle est lente et universellement mal vue.
Vous avez une condition de concurrence critique et devez utiliser une transaction. Mais cela peut être entièrement fait dans le code.
L'essentiel est de vous demander:
Si ce n'est pas le cas, insérez-le et jetez une exception. Mais gérez simplement l'exception comme toute autre erreur pouvant survenir.
Si vous vous attendez à ce que cela se produise, cela n’est PAS exceptionnel et vous devriez vérifier si l’enfant existe en premier, en répondant avec le message convivial approprié, dans le cas contraire.
Edit - Il semble y avoir beaucoup de controverse à ce sujet. Avant que vous ne votiez vers le bas, considérez:
A. Et s'il y avait deux contraintes FK. Recommanderiez-vous l'analyse du message d'exception pour déterminer quel objet manquait?
B. Si vous avez un échec, une seule instruction SQL est exécutée. Ce ne sont que les hits qui entraînent la dépense supplémentaire d'une seconde requête.
C. Habituellement, Id serait une clé de substitution. Il est difficile d’imaginer une situation dans laquelle vous en connaissez une et où vous n'êtes pas sûr que ce soit sur la base de données. Vérifier serait étrange. Mais que se passe-t-il s'il s'agit d'une clé naturelle saisie par l'utilisateur? Cela pourrait avoir une grande chance de ne pas être présent
la source
La vérification de l'unicité et du réglage est un anti-modèle; il peut toujours arriver que l'ID soit inséré simultanément entre l'heure de contrôle et l'heure d'écriture. Les bases de données sont équipées pour traiter ce problème par le biais de mécanismes tels que des contraintes et des transactions; la plupart des langages de programmation ne le sont pas. Par conséquent, si vous accordez de la valeur à la cohérence des données, laissez-le à l'expert (la base de données), c'est-à-dire faites l'insertion et capturez une exception si cela se produit.
la source
Je pense que ce que vous appelez «échec rapide» et ce que j'appelle n'est pas la même chose.
Dire à la base de données de faire un changement et gérer l'échec, c'est rapide. Votre chemin est compliqué, lent et pas particulièrement fiable.
Votre technique n’est pas un échec rapide, c’est un contrôle en amont. Il y a parfois de bonnes raisons, mais pas lorsque vous utilisez une base de données.
la source
Cela a commencé comme un commentaire mais est devenu trop grand.
Non, comme l'ont indiqué les autres réponses, ce schéma ne doit pas être utilisé. *
Lorsqu'il s'agit de systèmes utilisant des composants asynchrones, il y aura toujours une situation de concurrence critique dans laquelle la base de données (ou le système de fichiers ou un autre système asynchrone) peut changer entre la vérification et la modification. Une vérification de ce type n’est tout simplement pas un moyen fiable d’empêcher le type d’erreur que vous ne voulez pas traiter.
Pire que ne pas être suffisant, cela donne en un coup d’œil l’impression qu’il faut éviter que le double des enregistrements ne donne un faux sentiment de sécurité.
De toute façon, vous avez besoin du traitement des erreurs.
Dans les commentaires, vous avez demandé si vous aviez besoin de données provenant de plusieurs sources.
Toujours pas.
La question fondamentale ne disparaît pas si ce que vous voulez vérifier devient plus complexe.
De toute façon, vous avez toujours besoin du traitement des erreurs.
Même si cette vérification était un moyen fiable d’éviter l’erreur particulière contre laquelle vous essayez de vous protéger, d’autres erreurs peuvent quand même se produire. Que se passe-t-il si vous perdez la connexion à la base de données, si elle manque d'espace ou?
De toute façon, vous avez probablement toujours besoin d'une autre gestion d'erreur liée à la base de données. Le traitement de cette erreur particulière devrait probablement en être une petite partie.
Si vous avez besoin de données pour déterminer ce qu’il faut changer, vous devrez évidemment les collecter quelque part. (En fonction des outils que vous utilisez, il existe probablement de meilleurs moyens que de les collecter séparément.) Si, en examinant les données que vous avez collectées, vous déterminez que vous n'avez pas besoin de faire le changement après tout, tant mieux, ne faites pas de même. changement. Cette détermination est complètement distincte des problèmes de traitement des erreurs.
De toute façon, vous avez toujours besoin du traitement des erreurs.
Je sais que je suis répétitif, mais j'estime qu'il est important de le préciser. J'ai nettoyé ce gâchis avant.
Cela finira par échouer. En cas d'échec, il sera difficile et long d'aller au fond des choses. Il est difficile de résoudre les problèmes liés aux conditions de course. Ils ne se produisent pas régulièrement, il sera donc difficile voire impossible de reproduire de manière isolée. Pour commencer, vous n'avez pas mis en place la gestion correcte des erreurs, vous n'aurez donc probablement pas grand chose à faire: peut-être qu'un utilisateur final rapportera un texte crypté (que vous tentiez d'empêcher de voir dès le départ). Une trace de pile qui renvoie peut-être à cette fonction et qui nie de façon flagrante l'erreur devrait même être possible.
* Il peut exister des raisons commerciales valables pour exécuter ces vérifications existantes, par exemple pour empêcher l'application de dupliquer un travail coûteux, mais cela ne constitue pas une solution de remplacement appropriée pour une gestion correcte des erreurs.
la source
Je pense qu’un élément secondaire à noter ici - une des raisons pour lesquelles vous voulez que cela soit afin que vous puissiez formater un message d’erreur pour que l’utilisateur puisse le voir.
Je vous recommande vivement de:
a) montrer à l'utilisateur final le même message d'erreur générique pour chaque erreur qui se produit.
b) enregistrez l'exception réelle à un endroit auquel seuls les développeurs peuvent accéder (si sur un serveur) ou à un endroit qui peut vous être envoyé par des outils de rapport d'erreur (si le client est déployé)
c) n'essayez pas de formater les détails de l'exception d'erreur que vous enregistrez, sauf si vous pouvez ajouter d'autres informations utiles. Vous ne voulez pas avoir accidentellement «formaté» la seule information utile que vous auriez pu utiliser pour suivre un problème.
En bref, les exceptions regorgent d’informations techniques très utiles. Cela ne devrait en aucun cas être destiné à l'utilisateur final et vous perdez cette information à vos risques et périls.
la source