Aller au contenu

EntityFramework et l’enfer des erreurs de validations

Ou comment une entité erronée a mis à terre l’intégralité de mon batch de traitement et a ruiné 6 heures de traitement et m’a couté plusieurs heures de recherches.

Quel est donc ce batch que je ne saurais voir ?

Contextualisons un peu le problème. Je suis actuellement en train de travailler sur un batch de reporting et de génération de document à destination de très nombreuses entreprises (oui, c’est beaucoup de mot pour dire publipostage). Et au vu du très grand nombre de lettres devant être générés, nous avons l’idée de créer un cache dans l’application, des informations provenant d’un autre service, afin d’éviter de l’appeler à chaque fois que l’on en a besoin.

Jusque là, rien de bien complexe ni de bien optimisé, pas de Redis ou autre, un bête cache en base de données basé sur une clef unique, à côté duquel on enregistre un nom et un prénom.

Une erreur de validation, et c’est tout un monde qui s’écroule

Lors de nos tests, tout fonctionnait correctement, mais vous vous doutez bien que si je prends la peine d’en faire un article, c’est que tout ceci n’a pas duré. Au moment de passer aux tests grandeur nature, horreur et décadence, notre Seq est rempli d’erreur de partout, tout est rouge, rien ne fonctionne, plus rien ne s’enregistre en db. Et toujours le même message d’erreur (il me hante toujours) : « Validation failed for one or more entities. See ‘EntityValidationErrors’ property for more details. »

Je fais des recherches, je vérifie chaque exception enregistrée, je regarde si les services auxquels je fais appel fonctionnent toujours bien, et oui, ils fonctionnent toujours bien. Je tente d’autres choses, mais toujours rien, toujours des erreurs en très grande quantité !

Et soudain, l’illumination, et si tout était lié ?

Et si en fait, c’était toujours la même entité qui posait problème ? Et si en fait, EntityFramework conservait dans sa liste des entités à enregistrer, celle qui est en erreur de validation (spoiler: oui) ? Quelques recherches plus tard, je me rends compte que je ne suis pas le seul à avoir le problème, et je comprends que oui, tout est lié, toutes ces erreurs sont en faites dues à une seule entité qui ne peut pas être enregistrée.

Et bien oui, c’est le cas. Et tant que l’on garde le même contexte, on garde les mêmes entités à enregistrer, et chaque appel à SaveChanges() relance le même processus de validation, et fatalement, les mêmes erreurs (vu qu’aucun traitement n’est fait dessus). Il reste cependant à savoir, pourquoi une entité aussi simple peut poser problème ?

Tout n’est qu’une histoire de prénom

Le saviez-vous, certaines personnes n’ont pas de prénom ! Non ? Et bien moi non plus, c’est donc avec une confiance extrême que j’ai validé cette table où les deux champs étaient en Not Null. Et pourtant, une fois arrivé à ce monsieur Doe, plus aucun enregistrement ne pouvait être fait, bloquant des processus entiers de reporting.

Quand soudain, la solution m’eut apparu

Maintenant que j’avais clairement pu identifier le problème et ses causes, j’ai enfin pu me lancer dans la résolution de celui-ci ! Remplacer EntityFramework ? Faire une gestion d’erreur au top ? Vérifier que toutes les données sont correctes avant de les enregistrer ?

Non, bien trop propre ! On va juste se contenter d’arrêter de tracker les entités posant problème, et nous voilà donc avec ce code, qui se contente maintenant d’arrêter de tracker les entités en erreur de validation, et permet donc à toutes les autres entités de s’enregistrer !

var personneCache = new PersonneCache();
_context.PersonnesCache.Add(personneCache);
try
{
    _context.SaveChanges();
}
catch (Exception)
{
    // Quand une erreur est détectée, on détache l’entité et on relance l’exception pour laisser au reste du système de faire son traitement désiré
    _context.Entry(personneCache).State = EntityState.Detached;
    throw;
}

Quelles leçons en tirer ?

J’ai appris plusieurs choses de ce problème :

  • Une personne peut ne pas avoir de prénom, ou de nom, ou autre chose que l’on penserait indispensable. Une erreur de donnée est si vite arrivée, j’ai appris à toujours faire attention et ne pas partir du principe que tout sera rempli, surtout quand les données viennent d’un service externe
  • Tant qu’on reste dans le même contexte, on garde la même liste d’entité dans la transaction, il faut donc bien faire attention et traiter correctement les erreurs de validations. Comme quoi, on en apprend tous les jours sur ses outils 🙂
  • EntityFramework n’est peut-être pas le meilleur outil pour enregistrer un très grand nombre de données, il faudrait surement lui préférer une autre alternative, telle que Dapper.

Et vous, quelles erreurs avez-vous rencontrées, qui vous ont fait dire « Tiens, au final je ne le connais pas si bien que ça cet outil que j’utilise pourtant depuis des années ! » ?

Sources

Publié dansC#EntityFramework

Soyez le premier à commenter

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

0 Partages
Partagez
Tweetez
Partagez