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 ! » ?
Be First to Comment