Pregunta La validación falló para una o más entidades. Consulte la propiedad 'EntityValidationErrors' para más detalles


Estoy teniendo este error al sembrar mi base de datos con el primer acercamiento del código.

La validación falló para una o más entidades. Consulte la propiedad 'EntityValidationErrors' para más detalles.

Para ser sincero, no sé cómo verificar el contenido de los errores de validación. Visual Studio me muestra que es una matriz con 8 objetos, por lo que 8 errores de validación.

Esto estaba funcionando con mi modelo anterior, pero hice algunos cambios que explico a continuación:

  • Tenía un enum llamado Status, lo cambié a una clase llamada Status
  • Cambié la clase ApplicantsPositionHistory por tener 2 claves externas en la misma tabla

Discúlpenme por el código largo, pero tengo que pegarlo todo. La excepción se arroja en la última línea del siguiente código.

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


origen


Respuestas:


Para ser sincero, no sé cómo verificar el contenido de los errores de validación. Visual Studio me muestra que es una matriz con 8 objetos, por lo que 8 errores de validación.

De hecho, debería ver los errores si explora esa matriz en Visual Studio durante la depuración. Pero también puede detectar la excepción y luego escribir los errores en alguna tienda de registro o en la consola:

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 es una colección que representa las entidades que no se pudieron validar con éxito, y la colección interna ValidationErrors por entidad es una lista de errores en el nivel de propiedad.

Estos mensajes de validación suelen ser lo suficientemente útiles para encontrar el origen del problema.

Editar

Algunas pequeñas mejoras:

los valor de la propiedad ofensiva se puede incluir en el bucle interno como así:

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

Mientras se depura Debug.Write podría ser preferible sobre Console.WriteLine ya que funciona en todo tipo de aplicaciones, no solo en aplicaciones de consola (gracias a @Bart por su nota en los comentarios a continuación).

Para aplicaciones web que están en producción y que usan Elmah para el registro de excepciones, resultó ser muy útil para crear una excepción personalizada y sobrescribir SaveChanges para arrojar esta nueva excepción.

El tipo de excepción personalizada se ve así:

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

Y SaveChanges se puede sobrescribir de la siguiente manera:

public class MyContext : DbContext
{
    // ...

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

Algunas observaciones:

  • La pantalla de error amarilla que muestra Elmah en la interfaz web o en los correos electrónicos enviados (si ya la configuró) ahora muestra los detalles de validación directamente en la parte superior del mensaje.

  • Sobrescribir el Message propiedad en la excepción personalizada en lugar de sobrescribir ToString() tiene el beneficio de que la "Pantalla de muerte amarilla (YSOD)" de ASP.NET estándar también muestra este mensaje. A diferencia de Elmah, el YSOD aparentemente no usa ToString(), pero ambos muestran el Message propiedad.

  • Envolviendo el original DbEntityValidationException ya que la excepción interna asegura que la traza original de la pila seguirá estando disponible y se muestra en Elmah y YSOD.

  • Al establecer un punto de interrupción en la línea throw newException; simplemente puede inspeccionar el newException.Message la propiedad como texto en lugar de perforar en las colecciones de validación, que es un poco incómoda y no parece funcionar fácilmente para todos (ver comentarios a continuación).


1072
2017-10-17 19:03



Puede hacerlo desde Visual Studio durante la depuración sin escribir ningún código, ni siquiera un bloque catch.

Solo agrega un reloj con el nombre:

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

La expresión del reloj $exception muestra cualquier excepción lanzada en el contexto actual, incluso si no se ha capturado y asignado a una variable.

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


388
2017-07-02 22:51



Esto realmente podría hacerlo sin tener que escribir código:

En su bloque catch, agregue un punto de interrupción en la siguiente línea de código:

catch (Exception exception)
{

}

Ahora si pasas el mouse sobre exception o agregarlo a la Watch y luego navegue hacia los detalles de la excepción como se muestra a continuación; Verá qué columna (s) particular (es) está causando el problema, ya que este error generalmente ocurre cuando se viola una restricción de tabla.

enter image description here

Imagen grande


83
2018-03-25 15:09



Así es como puedes verificar el contenido de la EntityValidationErrors en Visual Studio (sin escribir ningún código adicional), es decir, durante Depuración en el IDE.

¿El problema?

Tienes razón, el depurador de Visual Studio Ver detalles Popup no muestra los errores reales dentro del EntityValidationErrors colección.

enter image description here

¡La solución!

Solo agregue la siguiente expresión en un Reloj rápido ventana y clic Reevaluar.

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

En mi caso, mira cómo puedo expandirme en el ValidationErrors  List dentro de EntityValidationErrors colección

enter image description here

Referencias  la publicación de blog mattrandle.me, la respuesta de @ yoel


40
2017-11-22 02:16



Para una forma rápida de ver el primer error sin siquiera agregar un reloj, puede pegarlo en la ventana Inmediato:

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

35
2017-09-17 12:58



Para cualquiera que trabaje en 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



Mientras está en modo de depuración dentro del catch {...} bloquear abrir la ventana "QuickWatch" (ctrl+alt+q) y pegue allí:

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

o:

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

Si no está en try / catch o no tiene acceso al objeto de excepción.

Esto te permitirá profundizar en el ValidationErrors árbol. Es la forma más fácil que he encontrado para obtener una visión instantánea de estos errores.


11
2017-08-27 01:29



Si simplemente está atrapando una excepción genérica, puede ser beneficioso que la presente como una DbEntityValidationException. Este tipo de excepción tiene una propiedad de Errores de validación, y al continuar expandiéndose hacia ellos, encontrará todos los problemas.

Por ejemplo, si pones un punto de quiebre en la captura, puedes lanzar lo siguiente en un reloj:

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

Un ejemplo de error es si un campo no permite valores nulos, y tiene una cadena nula, verá que dice que el campo es obligatorio.


10
2018-06-12 19:17



En la depuración, puede ingresar esto en el campo de entrada del evaluador de expresiones QuickWatch:

context.GetValidationErrors()

9
2018-02-08 18:09



simplemente compruebe la longitud del campo de la tabla de la base de datos. Su texto de entrada es mayor que la longitud del tipo de datos del campo columna


8
2018-03-09 05:11



Tuve que escribir esto en la ventana Inmediato: 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]

para profundizar en el error exacto!


7
2017-09-14 09:03