web-dev-qa-db-fra.com

DbEntityValidationException - Comment puis-je facilement savoir la cause de l'erreur?

J'ai un projet qui utilise Entity Framework. En appelant SaveChanges sur ma DbContext, j'obtiens l'exception suivante:

System.Data.Entity.Validation.DbEntityValidationException: Validation échoué pour une ou plusieurs entités. Voir la propriété 'EntityValidationErrors' pour plus de détails.

Tout va bien, mais je ne veux pas attacher un débogueur à chaque fois que cette exception se produit. De plus, dans les environnements de production, je ne peux pas facilement attacher un débogueur, je dois donc faire de gros efforts pour reproduire ces erreurs. 

Comment puis-je voir les détails cachés dans la DbEntityValidationException?

205
Martin Devillers

La solution la plus simple consiste à remplacer SaveChanges dans votre classe d'entités. Vous pouvez intercepter la DbEntityValidationException, décompresser les erreurs réelles et créer une nouvelle DbEntityValidationException avec le message amélioré.

  1. Créez une classe partielle à côté de votre fichier SomethingSomething.Context.cs.
  2. Utilisez le code au bas de ce post. 
  3. C'est tout. Votre implémentation utilisera automatiquement les SaveChanges remplacés sans aucun travail de refactor.

Votre message d'exception ressemblera maintenant à ceci:

System.Data.Entity.Validation.DbEntityValidationException: Validation échoué pour une ou plusieurs entités. Voir la propriété 'EntityValidationErrors' pour plus de détails. Les erreurs de validation sont les suivantes: Le champ PhoneNumber doit être une chaîne ou un type de tableau avec une longueur maximale de '12'; Le Le champ Nom est requis.

Vous pouvez supprimer les SaveChanges remplacés dans n’importe quelle classe qui hérite de DbContext:

public partial class SomethingSomethingEntities
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            // Retrieve the error messages as a list of strings.
            var errorMessages = ex.EntityValidationErrors
                    .SelectMany(x => x.ValidationErrors)
                    .Select(x => x.ErrorMessage);
    
            // Join the list to a single string.
            var fullErrorMessage = string.Join("; ", errorMessages);
    
            // Combine the original exception message with the new one.
            var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
    
            // Throw a new DbEntityValidationException with the improved exception message.
            throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
        }
    }
}

DbEntityValidationException contient également les entités qui ont provoqué les erreurs de validation. Ainsi, si vous avez besoin d'informations supplémentaires, vous pouvez modifier le code ci-dessus pour générer des informations sur ces entités.

Voir aussi: http://devillers.nl/improving-dbentityvalidationexception/

413
Martin Devillers

Comme Martin l'a indiqué, la DbEntityValidationResult contient plus d'informations. J'ai trouvé utile d'obtenir à la fois mon nom de classe POCO et mon nom de propriété, et je voulais éviter d'avoir à écrire des attributs ErrorMessage personnalisés sur toutes mes balises [Required] rien que pour cela.

Le code suivant de Tweak au code de Martin s'est occupé de ces détails pour moi:

// Retrieve the error messages as a list of strings.
List<string> errorMessages = new List<string>();
foreach (DbEntityValidationResult validationResult in ex.EntityValidationErrors)
{
    string entityName = validationResult.Entry.Entity.GetType().Name;
    foreach (DbValidationError error in validationResult.ValidationErrors)
    {
        errorMessages.Add(entityName + "." + error.PropertyName + ": " + error.ErrorMessage);
    }
}
44
Eric Hirst

Pour afficher la collection EntityValidationErrors, ajoutez l'expression de surveillance suivante à la fenêtre de surveillance.

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors

J'utilise Visual Studio 2013

41
Shehab Fawzy

Pendant que vous êtes en mode débogage dans le bloc catch {...}, ouvrez la fenêtre "QuickWatch" (ctrl+alt+q) et coller dedans:

((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors

Cela vous permettra d’avancer dans l’arborescence ValidationErrorsname__. C'est le moyen le plus simple que j'ai trouvé d'obtenir un aperçu instantané de ces erreurs.

Pour les utilisateurs de Visual 2012+ qui se soucient uniquement de la première erreur et qui peuvent ne pas avoir de bloc catchname__, vous pouvez même effectuer les opérations suivantes:

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors.First().ValidationErrors.First().ErrorMessage
13
GONeale

Pour trouver rapidement un message d'erreur significatif en inspectant l'erreur lors du débogage:

  • Ajouter une surveillance rapide pour:

    ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
    
  • Accédez à EntityValidationErrors de la manière suivante:

    (élément de collecte, par exemple [0])> ValidationErrors> (élément de collection, par exemple [0])> ErrorMessage

9
Chris Halcrow

En fait, il ne s'agit que du problème de validation, EF validera d'abord les propriétés de l'entité avant d'apporter des modifications à la base de données . EF vérifiera donc si la valeur de la propriété est hors limites, comme lors de la conception de la table. Table_Column_UserName est varchar (20). Mais, dans EF, vous avez entré une valeur supérieure à 20.. Ou, dans d'autres cas, si la colonne ne permet pas d'être Null . Dans le processus de validation, vous devez donc définir une valeur. à la colonne non nulle, que vous fassiez le changement ou non ..__ Personnellement, j'aime la réponse de Leniel Macaferi. Il peut vous montrer le détail des problèmes de validation

5
Calvin

Je pense que "Les erreurs de validation réelles" peuvent contenir des informations sensibles, ce qui pourrait expliquer pourquoi Microsoft a choisi de les placer dans un autre endroit (propriétés). La solution indiquée ici est pratique, mais il convient de la prendre avec prudence.

Je préférerais créer une méthode d'extension. Plus de raisons à cela: 

  • Conserver la trace de la pile d'origine
  • Suivez le principe ouvert/fermé (par exemple, je peux utiliser différents messages pour différents types de journaux) 
  • Dans les environnements de production, il peut y avoir d'autres endroits (par exemple: autre dbcontext) où une exception DbEntityValidationException peut être levée.
3
Luis Toapanta

Pour Azure Functions, nous utilisons cette simple extension vers Microsoft.Extensions.Logging.ILogger  

public static class LoggerExtensions
{
    public static void Error(this ILogger logger, string message, Exception exception)
    {
        if (exception is DbEntityValidationException dbException)
        {
            message += "\nValidation Errors: ";
            foreach (var error in dbException.EntityValidationErrors.SelectMany(entity => entity.ValidationErrors))
            {
                message += $"\n * Field name: {error.PropertyName}, Error message: {error.ErrorMessage}";
            }
        }

        logger.LogError(default(EventId), exception, message);
    }
}

et exemple d'utilisation: 

try
{
    do something with request and EF
}
catch (Exception e)
{
    log.Error($"Failed to create customer due to an exception: {e.Message}", e);
    return await StringResponseUtil.CreateResponse(HttpStatusCode.InternalServerError, e.Message);
}
0
Juri

Utilisez try block dans votre code comme

try
{
    // Your code...
    // Could also be before try if you know the exception occurs in SaveChanges

    context.SaveChanges();
}
catch (DbEntityValidationException e)
{
    foreach (var eve in e.EntityValidationErrors)
    {
        Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
            eve.Entry.Entity.GetType().Name, eve.Entry.State);
        foreach (var ve in eve.ValidationErrors)
        {
            Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
                ve.PropertyName, ve.ErrorMessage);
        }
    }
    throw;
}

Vous pouvez vérifier les détails ici aussi

  1. http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/

  2. La validation a échoué pour une ou plusieurs entités. Voir la propriété 'EntityValidationErrors' pour plus de détails

  3. http://blogs.infosupport.com/improving-dbentityvalidationexception/

0
Atta H.