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


J'ai cette erreur lors de l'ensemencement de ma base de données avec le code d'abord.

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

Pour être honnête, je ne sais pas comment vérifier le contenu des erreurs de validation. Visual Studio me montre que c'est un tableau avec 8 objets, donc 8 erreurs de validation.

Cela fonctionnait avec mon modèle précédent, mais j'ai apporté quelques modifications que j'explique ci-dessous:

  • J'ai eu un enum appelé Status, je l'ai changé en une classe appelée Status
  • J'ai changé la classe ApplicantsPositionHistory pour avoir 2 clé étrangère à la même table

Excusez-moi pour le long code, mais je dois tout coller. L'exception est renvoyée dans la dernière ligne du code suivant.

namespace Data.Model
{  
    public class Position
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]   
        public int PositionID { get; set; }

        [Required(ErrorMessage = "Position name is required.")]
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Name should not be longer than 20 characters.")]
        [Display(Name = "Position name")]              
        public string name { get; set; }

        [Required(ErrorMessage = "Number of years is required")] 
        [Display(Name = "Number of years")]        
        public int yearsExperienceRequired { get; set; }

        public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
    }

    public class Applicant
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]      
        public int ApplicantID { get; set; }

        [Required(ErrorMessage = "Name is required")] 
        [StringLength(20, MinimumLength = 3, ErrorMessage="Name should not be longer than 20 characters.")]
        [Display(Name = "First and LastName")]
        public string name { get; set; }

        [Required(ErrorMessage = "Telephone number is required")] 
        [StringLength(10, MinimumLength = 3, ErrorMessage = "Telephone should not be longer than 20 characters.")]
        [Display(Name = "Telephone Number")]
        public string telephone { get; set; }

        [Required(ErrorMessage = "Skype username is required")] 
        [StringLength(10, MinimumLength = 3, ErrorMessage = "Skype user should not be longer than 20 characters.")]
        [Display(Name = "Skype Username")]
        public string skypeuser { get; set; }

        public byte[] photo { get; set; }

        public virtual ICollection<ApplicantPosition> applicantPosition { get; set; }
    }

    public class ApplicantPosition
    {
        [Key]
        [Column("ApplicantID", Order = 0)]
        public int ApplicantID { get; set; }

        [Key]
        [Column("PositionID", Order = 1)]
        public int PositionID { get; set; }

        public virtual Position Position { get; set; }

        public virtual Applicant Applicant { get; set; }

        [Required(ErrorMessage = "Applied date is required")] 
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Display(Name = "Date applied")]     
        public DateTime appliedDate { get; set; }

        [Column("StatusID", Order = 0)]
        public int StatusID { get; set; }

        public Status CurrentStatus { get; set; }

        //[NotMapped]
        //public int numberOfApplicantsApplied
        //{
        //    get
        //    {
        //        int query =
        //             (from ap in Position
        //              where ap.Status == (int)Status.Applied
        //              select ap
        //                  ).Count();
        //        return query;
        //    }
        //}
    }

    public class Address
    {
        [StringLength(20, MinimumLength = 3, ErrorMessage = "Country should not be longer than 20 characters.")]
        public string Country { get; set; }

        [StringLength(20, MinimumLength = 3, ErrorMessage = "City  should not be longer than 20 characters.")]
        public string City { get; set; }

        [StringLength(50, MinimumLength = 3, ErrorMessage = "Address  should not be longer than 50 characters.")]
        [Display(Name = "Address Line 1")]     
        public string AddressLine1 { get; set; }

        [Display(Name = "Address Line 2")]
        public string AddressLine2 { get; set; }   
    }

    public class ApplicationPositionHistory
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
        public int ApplicationPositionHistoryID { get; set; }

        public ApplicantPosition applicantPosition { get; set; }

        [Column("oldStatusID")]
        public int oldStatusID { get; set; }

        [Column("newStatusID")]
        public int newStatusID { get; set; }

        public Status oldStatus { get; set; }

        public Status newStatus { get; set; }

        [StringLength(500, MinimumLength = 3, ErrorMessage = "Comments  should not be longer than 500 characters.")]
        [Display(Name = "Comments")]
        public string comments { get; set; }

        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        [Display(Name = "Date")]     
        public DateTime dateModified { get; set; }
    }

    public class Status
    {
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
        public int StatusID { get; set; }

        [StringLength(20, MinimumLength = 3, ErrorMessage = "Status  should not be longer than 20 characters.")]
        [Display(Name = "Status")]
        public string status { get; set; }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.IO;

namespace Data.Model
{
    public class HRContextInitializer : DropCreateDatabaseAlways<HRContext>
    {
        protected override void Seed(HRContext context)
        {
            #region Status
            Status applied = new Status() { status = "Applied" };
            Status reviewedByHR = new Status() { status = "Reviewed By HR" };
            Status approvedByHR = new Status() { status = "Approved by HR" };
            Status rejectedByHR = new Status() { status = "Rejected by HR" };
            Status assignedToTechnicalDepartment = new Status() { status = "Assigned to Technical Department" };
            Status approvedByTechnicalDepartment = new Status() { status = "Approved by Technical Department" };
            Status rejectedByTechnicalDepartment = new Status() { status = "Rejected by Technical Department" };

            Status assignedToGeneralManager = new Status() { status = "Assigned to General Manager" };
            Status approvedByGeneralManager = new Status() { status = "Approved by General Manager" };
            Status rejectedByGeneralManager = new Status() { status = "Rejected by General Manager" };

            context.Status.Add(applied);
            context.Status.Add(reviewedByHR);
            context.Status.Add(approvedByHR);
            context.Status.Add(rejectedByHR);
            context.Status.Add(assignedToTechnicalDepartment);
            context.Status.Add(approvedByTechnicalDepartment);
            context.Status.Add(rejectedByTechnicalDepartment);
            context.Status.Add(assignedToGeneralManager);
            context.Status.Add(approvedByGeneralManager);
            context.Status.Add(rejectedByGeneralManager); 
            #endregion    

            #region Position
            Position netdeveloper = new Position() { name = ".net developer", yearsExperienceRequired = 5 };
            Position javadeveloper = new Position() { name = "java developer", yearsExperienceRequired = 5 };
            context.Positions.Add(netdeveloper);
            context.Positions.Add(javadeveloper); 
            #endregion

            #region Applicants
            Applicant luis = new Applicant()
            {
                name = "Luis",
                skypeuser = "le.valencia",
                telephone = "0491732825",
                photo = File.ReadAllBytes(@"C:\Users\LUIS.SIMBIOS\Documents\Visual Studio 2010\Projects\SlnHR\HRRazorForms\Content\pictures\1.jpg")
            };

            Applicant john = new Applicant()
            {
                name = "John",
                skypeuser = "jo.valencia",
                telephone = "3435343543",
                photo = File.ReadAllBytes(@"C:\Users\LUIS.SIMBIOS\Documents\Visual Studio 2010\Projects\SlnHR\HRRazorForms\Content\pictures\2.jpg")
            };

            context.Applicants.Add(luis);
            context.Applicants.Add(john); 
            #endregion

            #region ApplicantsPositions
            ApplicantPosition appicantposition = new ApplicantPosition()
            {
                Applicant = luis,
                Position = netdeveloper,
                appliedDate = DateTime.Today,
                StatusID = 1
            };

            ApplicantPosition appicantposition2 = new ApplicantPosition()
            {
                Applicant = john,
                Position = javadeveloper,
                appliedDate = DateTime.Today,
                StatusID = 1
            };        

            context.ApplicantsPositions.Add(appicantposition);            
            context.ApplicantsPositions.Add(appicantposition2); 
            #endregion

            context.SaveChanges(); --->> Error here
        }
    }
}


671
2017-10-17 14:33


origine


Réponses:


Pour être honnête, je ne sais pas comment vérifier le contenu des erreurs de validation. Visual Studio me montre que c'est un tableau avec 8 objets, donc 8 erreurs de validation.

En fait, vous devriez voir les erreurs si vous explorez ce tableau dans Visual Studio pendant le débogage. Mais vous pouvez également intercepter l'exception, puis écrire les erreurs dans un magasin de journalisation ou dans la console:

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;
}

EntityValidationErrors est une collection qui représente les entités qui n'ont pas pu être validées avec succès, et la collection interne ValidationErrors par entité est une liste d'erreurs au niveau de la propriété.

Ces messages de validation sont généralement suffisamment utiles pour trouver la source du problème.

modifier

Quelques légères améliorations:

le valeur de la propriété incriminée peut être inclus dans la boucle interne comme suit:

        foreach (var ve in eve.ValidationErrors)
        {
            Console.WriteLine("- Property: \"{0}\", Value: \"{1}\", Error: \"{2}\"",
                ve.PropertyName,
                eve.Entry.CurrentValues.GetValue<object>(ve.PropertyName),
                ve.ErrorMessage);
        }

Pendant le débogage Debug.Write pourrait être préférable à Console.WriteLine comme il fonctionne dans toutes sortes d'applications, pas seulement les applications console (merci à @Bart pour sa note dans les commentaires ci-dessous).

Pour les applications Web en production et qui utilisent Elmah pour la journalisation des exceptions, il s'est avéré très utile pour moi de créer une exception personnalisée et écraser SaveChanges afin de jeter cette nouvelle exception.

Le type d'exception personnalisée ressemble à ceci:

public class FormattedDbEntityValidationException : Exception
{
    public FormattedDbEntityValidationException(DbEntityValidationException innerException) :
        base(null, innerException)
    {
    }

    public override string Message
    {
        get
        {
            var innerException = InnerException as DbEntityValidationException;
            if (innerException != null)
            {
                StringBuilder sb = new StringBuilder();

                sb.AppendLine();
                sb.AppendLine();
                foreach (var eve in innerException.EntityValidationErrors)
                {
                    sb.AppendLine(string.Format("- Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                        eve.Entry.Entity.GetType().FullName, eve.Entry.State));
                    foreach (var ve in eve.ValidationErrors)
                    {
                        sb.AppendLine(string.Format("-- Property: \"{0}\", Value: \"{1}\", Error: \"{2}\"",
                            ve.PropertyName,
                            eve.Entry.CurrentValues.GetValue<object>(ve.PropertyName),
                            ve.ErrorMessage));
                    }
                }
                sb.AppendLine();

                return sb.ToString();
            }

            return base.Message;
        }
    }
}

Et SaveChanges peut être écrasé de la manière suivante:

public class MyContext : DbContext
{
    // ...

    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            var newException = new FormattedDbEntityValidationException(e);
            throw newException;
        }
    }
}

Quelques remarques:

  • L'écran d'erreur jaune affiché par Elmah dans l'interface Web ou dans les e-mails envoyés (si vous l'avez configuré) affiche désormais les détails de validation directement en haut du message.

  • Remplacer le Message propriété dans l'exception personnalisée au lieu d'écraser ToString() a l'avantage que l'écran standard ASP.NET "Yellow screen of death (YSOD)" affiche également ce message. Contrairement à Elmah, l'YSOD n'utilise apparemment pas ToString(), mais les deux affichent Message propriété.

  • Envelopper l'original DbEntityValidationException Comme l'exception interne assure que la trace de la pile d'origine sera toujours disponible et est affichée dans Elmah et le YSOD.

  • En définissant un point d'arrêt sur la ligne throw newException; vous pouvez simplement inspecter le newException.Message propriété comme un texte au lieu de percer dans les collections de validation qui est un peu gênant et ne semble pas fonctionner facilement pour tout le monde (voir les commentaires ci-dessous).


1072
2017-10-17 19:03



Vous pouvez le faire à partir de Visual Studio pendant le débogage sans écrire de code, pas même un bloc catch.

Ajoutez simplement une montre avec le nom:

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

L'expression de la montre $exception affiche toute exception renvoyée dans le contexte actuel, même si elle n'a pas été interceptée et affectée à une variable.

Basé sur http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/


388
2017-07-02 22:51



Cela pourrait effectivement le faire sans avoir à écrire du code:

Dans votre bloc catch, ajoutez un point d'arrêt à la ligne de code suivante:

catch (Exception exception)
{

}

Maintenant, si vous passez la souris sur exception ou ajoutez-le à Watch puis naviguez dans les détails d'exception comme indiqué ci-dessous; vous verrez quelle (s) colonne (s) particulière (s) est / sont à l'origine du problème car cette erreur se produit généralement quand une contrainte de table est violée.

enter image description here

Grande image


83
2018-03-25 15:09



Voici comment vous pouvez vérifier le contenu du EntityValidationErrors dans Visual Studio (sans écrire de code supplémentaire), c'est-à-dire Débogage dans le IDE.

Le problème?

Vous avez raison, le débogueur Visual Studio Voir les détails Popup ne montre pas les erreurs réelles à l'intérieur du EntityValidationErrors collection .

enter image description here

La solution!

Ajoutez simplement l'expression suivante dans un Regarder rapidement fenêtre et cliquez Réévaluer.

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

Dans mon cas, voir comment je suis en mesure de développer dans le ValidationErrors  List à l'intérieur de EntityValidationErrors collection

enter image description here

Les références:  blog blog mattrandle.me, La réponse de @ yoel


40
2017-11-22 02:16



Pour un moyen rapide de voir la première erreur sans même ajouter une montre, vous pouvez le coller dans la fenêtre Exécution:

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

35
2017-09-17 12:58



Pour tous ceux qui travaillent dans VB.NET

Try
Catch ex As DbEntityValidationException
    For Each a In ex.EntityValidationErrors
        For Each b In a.ValidationErrors
            Dim st1 As String = b.PropertyName
            Dim st2 As String = b.ErrorMessage
        Next
    Next
End Try

14
2017-10-21 01:35



Lorsque vous êtes en mode débogage dans le catch {...} bloquer ouvrir la fenêtre "QuickWatch" (ctrl+alt+q) et collez-y:

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

ou:

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

Si vous n'êtes pas dans un essai / catch ou n'avez pas accès à l'objet d'exception.

Cela vous permettra d'explorer le ValidationErrors arbre. C'est la façon la plus simple que j'ai trouvée d'avoir un aperçu instantané de ces erreurs.


11
2017-08-27 01:29



Si vous attrapez simplement une exception générique, cela peut vous être utile DbEntityValidationException. Ce type d'exception a une propriété Validation Errors, et en continuant à vous y frayer un chemin, vous trouverez tous les problèmes.

Par exemple, si vous placez un point d'arrêt dans la capture, vous pouvez insérer ce qui suit dans une montre:

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

Un exemple d'erreur est Si un champ n'autorise pas les valeurs nulles, et que vous avez une chaîne nulle, vous verrez que le champ est obligatoire.


10
2018-06-12 19:17



Dans le débogage, vous pouvez entrer ceci dans votre champ de saisie d'évaluateur d'expression QuickWatch:

context.GetValidationErrors()

9
2018-02-08 18:09



Vérifiez simplement la longueur de votre champ de table de base de données. Votre texte d'entrée est supérieur à la longueur du type de données du champ de colonne


8
2018-03-09 05:11



Je devais écrire ceci dans la fenêtre Exécution: 3

(((exception as System.Data.Entity.Validation.DbEntityValidationException).EntityValidationErrors as System.Collections.Generic.List<System.Data.Entity.Validation.DbEntityValidationResult>)[0].ValidationErrors as System.Collections.Generic.List<System.Data.Entity.Validation.DbValidationError>)[0]

afin d'aller au fond de l'erreur exacte!


7
2017-09-14 09:03