DbEntityValidationException-Comment puis-je facilement dire ce qui a causé l'erreur?
j'ai un projet qui utilise le cadre D'entité. En appelant SaveChanges
sur mon DbContext
, j'obtiens l'exception suivante:
Système.Données.Entité.Validation.DbEntityValidationException: Validation échoué à une ou plusieurs entités. Voir la propriété "EntityValidationErrors" pour plus de détails.
tout cela est très bien, mais je ne veux pas attacher un débogueur à chaque fois que cette exception se produit. Plus de plus de, dans les environnements de production, Je ne peux pas facilement fixer un débogueur, donc je dois aller très loin pour reproduire ces erreurs.
Comment puis-je voir les détails cachés dans le DbEntityValidationException
?
9 réponses
la solution la plus simple est de surcharger SaveChanges
sur votre classe entities. Vous pouvez attraper le DbEntityValidationException
, déballer les erreurs réelles et créer un nouveau DbEntityValidationException
avec le message amélioré.
- créez une classe partielle à côté de votre quelque chose.Cadre.cs fichier.
- utilisez le code au bas de ce message.
- C'est ça. Votre mise en œuvre utilisera automatiquement les modifications de type SaveChanges sans aucun travail de remaniement.
votre message d'exception ressemblera maintenant à ceci:
Système.Données.Entité.Validation.DbEntityValidationException: Validation échoué à une ou plusieurs entités. Voir la propriété "EntityValidationErrors" pour plus de détails. Les erreurs de validation sont: le champ PhoneNumber doit être un type de chaîne ou de tableau d'une longueur maximale de "12"; Champ nom est obligatoire.
vous pouvez laisser tomber les SaveChanges dépassées 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);
}
}
}
le DbEntityValidationException
contient également les entités qui ont causé les erreurs de validation. Ainsi, si vous avez besoin d'encore plus d'informations, vous pouvez modifier le code ci-dessus pour produire des informations sur ces entités.
Voir aussi: http://devillers.nl/improving-dbentityvalidationexception/
comme Martin l'a indiqué, il y a plus d'information dans le DbEntityValidationResult
. J'ai trouvé utile d'obtenir à la fois mon nom de classe POCO et le nom de la propriété dans chaque message, et je voulais éviter d'avoir à écrire des attributs ErrorMessage
personnalisés sur toutes mes étiquettes [Required]
juste pour cela.
le tweak suivant 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);
}
}
pour visualiser la collection EntityValidationErrors
, ajouter l'expression Watch suivante à la fenêtre Watch.
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
j'utilise visual studio 2013
pendant que vous êtes en mode débogage dans le bloc catch {...}
ouvrez la fenêtre" QuickWatch " ( ctrl + alt + q ) et collez là:
((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
cela vous permettra de creuser dans l'arbre ValidationErrors
. C'est le moyen le plus facile que j'ai trouvé pour avoir un aperçu instantané de ces erreurs.
pour les utilisateurs Visual 2012+ qui se soucient seulement de la première erreur et pourraient ne pas avoir un bloc catch
, vous pouvez même faire:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors.First().ValidationErrors.First().ErrorMessage
pour trouver rapidement un message d'erreur significatif en inspectant l'erreur lors du débogage:
-
ajouter une montre rapide pour:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
-
Forez dans EntityValidationErrors like this:
(élément de collection par exemple [0]) > ValidationErrors > (élément de collection par exemple [0]) > ErrorMessage
en fait, il ne s'agit que du problème de validation, le FE validera d'abord les propriétés de l'entité avant d'apporter des modifications à la base de données. Donc, EF va vérifier si la valeur de la propriété est hors de portée, comme quand vous avez conçu la table. Table_Column_UserName est varchar (20). Mais, dans ce, vous avez entré une valeur qui dépasse 20. Ou, dans d'autres cas, si la colonne ne peut pas être Null. Ainsi, dans le processus de validation, vous devez définir une valeur pour la colonne not null, peu importe si vous allez faire le changement. Personnellement, J'aime la réponse de Leniel Macaferi. Il peut vous montrer le détail des questions de validation
je pense que" les erreurs de validation réelles " peuvent contenir des informations sensibles, et cela pourrait être la raison pour laquelle Microsoft a choisi de les mettre à un autre endroit (propriétés). La solution indiquée ici est pratique, mais elle doit être prise avec prudence.
je préférerais créer une méthode d'extension. D'autres raisons à cela:
- Garder l'original trace de la pile
- Suivez ouvert/fermé principe (ie.: Je peux utiliser différents messages pour les différents types de journaux)
- dans les environnements de production, il pourrait y avoir d'autres endroits (p. ex.: autre dbcontext) où une exception DbEntityValidationException pourrait être lancée.
pour les fonctions Azure nous utilisons cette extension simple à Microsoft.Extension.Journalisation.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);
}
utilisez le bloc essayer 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