c# - dbentityvalidationexception - entityvalidationerrors example




Convalida non riuscita per una o più entità. Vedi la proprietà 'EntityValidationErrors' per maggiori dettagli (17)

Per essere onesti, non so come controllare il contenuto degli errori di convalida. Visual Studio mi mostra che si tratta di un array con 8 oggetti, quindi 8 errori di convalida.

In realtà dovresti vedere gli errori se esegui il drill su quell'array in Visual Studio durante il debug. Ma puoi anche catturare l'eccezione e quindi scrivere gli errori in qualche archivio di registrazione o nella 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 è una raccolta che rappresenta le entità che non possono essere convalidate correttamente e la raccolta interna ValidationErrors per entità è una lista di errori a livello di proprietà.

Questi messaggi di convalida sono di solito abbastanza utili per trovare la fonte del problema.

modificare

Alcuni piccoli miglioramenti:

Il valore della proprietà incriminata può essere incluso nel ciclo interno in questo modo:

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

Mentre il debug di Debug.Write potrebbe essere preferibile su Console.WriteLine poiché funziona in tutti i tipi di applicazioni, non solo nelle applicazioni della console (grazie a @Bart per la sua nota nei commenti sotto).

Per le applicazioni Web in produzione e che utilizzano Elmah per la registrazione delle eccezioni, è stato molto utile per me creare un'eccezione personalizzata e sovrascrivere SaveChanges per lanciare questa nuova eccezione.

Il tipo di eccezione personalizzata si presenta così:

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

E SaveChanges può essere sovrascritto nel seguente modo:

public class MyContext : DbContext
{
    // ...

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

Alcune osservazioni:

  • La schermata gialla di errore mostrata da Elmah nell'interfaccia Web o nelle e-mail inviate (se questa è stata configurata) visualizza ora i dettagli di convalida direttamente nella parte superiore del messaggio.

  • Sovrascrivere la proprietà Message nell'eccezione personalizzata invece di sovrascrivere ToString() ha il vantaggio che lo standard ASP.NET "Yellow screen of death (YSOD)" visualizza anche questo messaggio. A differenza di Elmah, YSOD apparentemente non usa ToString() , ma entrambi visualizzano la proprietà Message .

  • Il DbEntityValidationException di DbEntityValidationException originale come eccezione interna garantisce che la traccia dello stack originale sia ancora disponibile e visualizzata in Elmah e YSOD.

  • Impostando un punto di interruzione sulla linea si throw newException; puoi semplicemente ispezionare la proprietà newException.Message come testo invece di eseguire il drilling nelle raccolte di convalida, che è un po 'scomodo e non sembra funzionare facilmente per tutti (vedi i commenti sotto).

Sto riscontrando questo errore quando ho seminato il mio database con il primo approccio al codice.

Convalida non riuscita per una o più entità. Vedi la proprietà 'EntityValidationErrors' per maggiori dettagli.

Per essere onesti, non so come controllare il contenuto degli errori di convalida. Visual Studio mi mostra che si tratta di un array con 8 oggetti, quindi 8 errori di convalida.

Questo funzionava con il mio modello precedente, ma ho apportato alcune modifiche che spiego di seguito:

  • Avevo un enum chiamato Status, l'ho cambiato in una classe chiamata Status
  • Ho cambiato la classe ApplicantsPositionHistory per avere 2 chiavi esterne per la stessa tabella

Scusami per il lungo codice, ma devo incollarlo tutto. L'eccezione è generata nell'ultima riga del seguente codice.

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

Cattura l'eccezione in un catch di prova e poi guarda rapidamente o ctrl + d & ctrl + q e puoi eseguire il drill-down su EntityValidationErrors.


Controlla i valori dei campi che stai passando, sono validi e in base ai campi del database. Ad esempio, il numero di caratteri passati in un campo particolare è inferiore ai caratteri definiti nel campo della tabella del database.


Controlla se non hai un vincolo Not Null nelle colonne della tabella e non passi il valore per quella colonna durante le operazioni di inserimento / aggiornamento. Che causa questa eccezione nel framework di entità.


Ecco un altro modo per farlo invece di usare i cicli foreach per guardare all'interno di EntityValidationErrors. Ovviamente puoi formattare il messaggio a tuo piacimento:

try {
        // your code goes here...
    } 
catch (DbEntityValidationException ex) 
    {
        Console.Write($"Validation errors: {string.Join(Environment.NewLine, ex.EntityValidationErrors.SelectMany(vr => vr.ValidationErrors.Select(err => $"{err.PropertyName} - {err.ErrorMessage}")))}", ex);
        throw;
    }

Ho affrontato questo errore prima

quando ho provato ad aggiornare il campo specifico nel mio modello in framwork di entità

Letter letter = new Letter {ID = letterId, ExportNumber = letterExportNumber,EntityState = EntityState.Modified};
LetterService.ChangeExportNumberfor(letter);
//----------


public int ChangeExportNumber(Letter letter)
    {
        int result = 0;
        using (var db = ((LettersGeneratorEntities) GetContext()))
        {
            db.Letters.Attach(letter);
            db.Entry(letter).Property(x => x.ExportNumber).IsModified = true;
            result += db.SaveChanges();
        }
        return result;
    }

e secondo le risposte di cui sopra

Ho trovato il messaggio di convalida The SignerName field is required.

che punta al campo nel mio modello

e quando ho controllato il mio schema di database ho trovato

quindi off coure ValidationException ha il diritto di aumentare

e secondo questo campo voglio che sia annullabile, (non so come l'ho incasinato)

quindi ho cambiato campo per consentire a Null, e con questo il mio codice non mi darà ancora questo errore

quindi questo errore potrebbe accadere se si invalida l'integrità dei dati del proprio database


La risposta di @Slauma è davvero eccezionale, ma ho scoperto che non funzionava quando una proprietà ComplexType non era valida.

Ad esempio, supponiamo di avere un Phone di proprietà del tipo complesso PhoneNumber . Se la proprietà AreaCode non è valida, il nome della proprietà in ve.PropertyNames è "Phone.AreaCode". Ciò causa il eve.Entry.CurrentValues<object>(ve.PropertyName) della chiamata a eve.Entry.CurrentValues<object>(ve.PropertyName) .

Per risolvere questo problema, puoi dividere il nome della proprietà su ciascuno . , quindi ricorrere alla matrice risultante di nomi di proprietà. Infine, quando arrivi in ​​fondo alla catena, puoi semplicemente restituire il valore della proprietà.

Di seguito è riportata la classe FormattedDbEntityValidationException @ Slauma con supporto per ComplexTypes.

Godere!

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

    public override string Message
    {
        get
        {
            var innerException = InnerException as DbEntityValidationException;
            if (innerException == null) return base.Message;

            var 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)
                {
                    object value;
                    if (ve.PropertyName.Contains("."))
                    {
                        var propertyChain = ve.PropertyName.Split('.');
                        var complexProperty = eve.Entry.CurrentValues.GetValue<DbPropertyValues>(propertyChain.First());
                        value = GetComplexPropertyValue(complexProperty, propertyChain.Skip(1).ToArray());
                    }
                    else
                    {
                        value = eve.Entry.CurrentValues.GetValue<object>(ve.PropertyName);
                    }
                    sb.AppendLine(string.Format("-- Property: \"{0}\", Value: \"{1}\", Error: \"{2}\"",
                        ve.PropertyName,
                        value,
                        ve.ErrorMessage));
                }
            }
            sb.AppendLine();

            return sb.ToString();
        }
    }

    private static object GetComplexPropertyValue(DbPropertyValues propertyValues, string[] propertyChain)
    {
        var propertyName = propertyChain.First();
        return propertyChain.Count() == 1 
            ? propertyValues[propertyName] 
            : GetComplexPropertyValue((DbPropertyValues)propertyValues[propertyName], propertyChain.Skip(1).ToArray());
    }
}

Mentre sei in modalità di debug all'interno del catch {...} , apri la finestra "QuickWatch" ( ctrl + alt + q ) e incollaci lì:

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

o:

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

Se non si è in un try / catch o non si ha accesso all'oggetto exception.

Ciò ti consentirà di eseguire il drill-down nell'albero ValidationErrors . È il modo più semplice che ho trovato per ottenere una visione immediata di questi errori.


Per @ risposta di Slauma e suggerimento di @ Milton ho esteso il metodo di salvataggio personalizzato della nostra classe base con un try / catch che gestirà (e quindi accederà alla nostra registrazione degli errori!) Questo tipo di eccezioni.

// Where `BaseDB` is your Entities object... (it could be `this` in a different design)
public void Save(bool? validateEntities = null)
{
    try
    {
        //Capture and set the validation state if we decide to
        bool validateOnSaveEnabledStartState = BaseDB.Configuration.ValidateOnSaveEnabled;
        if (validateEntities.HasValue)
            BaseDB.Configuration.ValidateOnSaveEnabled = validateEntities.Value;

        BaseDB.SaveChanges();

        //Revert the validation state when done
        if (validateEntities.HasValue)
            BaseDB.Configuration.ValidateOnSaveEnabled = validateOnSaveEnabledStartState;
    }
    catch (DbEntityValidationException e)
    {
        StringBuilder sb = new StringBuilder();
        foreach (var eve in e.EntityValidationErrors)
        {
            sb.AppendLine(string.Format("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)
            {
                sb.AppendLine(string.Format("- Property: \"{0}\", Error: \"{1}\"",
                                            ve.PropertyName,
                                            ve.ErrorMessage));
            }
        }
        throw new DbEntityValidationException(sb.ToString(), e);
    }
}

Per chiunque lavori in 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

Puoi farlo da Visual Studio durante il debug senza scrivere alcun codice, nemmeno un blocco catch.

Basta aggiungere un orologio con il nome:

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

L' $exception watch watch visualizza qualsiasi eccezione generata nel contesto corrente, anche se non è stata catturata e assegnata a una variabile.

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


Quello che ho trovato ... quando ho l'errore 'EntityValidationErrors' è che .... ho un campo nel mio database 'db1' nella tabella 'tbladdress' come 'address1' che ha dimensione di 100 (cioè indirizzo varchar (100) null) e stavo passando il valore più di 100 caratteri ... e questo ha comportato errori durante il salvataggio dei dati nel database ....

Quindi devi controllare i dati che stai passando al campo.


Questo potrebbe davvero farlo senza dover scrivere codice:

Nel blocco catch, aggiungi un punto di interruzione alla seguente riga di codice:

catch (Exception exception)
{

}

Ora, se si passa con il mouse su exception o lo si aggiunge Watch quindi navigare nei dettagli dell'eccezione come mostrato di seguito; vedrai quale / e particolare / e colonna / e sta causando il problema poiché questo errore di solito si verifica quando un vincolo di tabella viene violato.

Immagine grande


Se stai semplicemente rilevando un'eccezione generica, potrebbe essere utile eseguire il cast come DbEntityValidationException . Questo tipo di eccezione ha una proprietà Errori di convalida e, continuando ad espandervi, troverete tutti i problemi.

Ad esempio, se inserisci un punto di interruzione nella cattura, puoi lanciare quanto segue in un orologio:

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

Un esempio di errore è che se un campo non consente valori nulli e si dispone di una stringa nulla, lo si vedrà affermare che il campo è obbligatorio.


Si noti che Entity.GetType().BaseType.Name fornisce il nome del tipo specificato, non quello con tutte le cifre esadecimali nel suo nome.


Sto solo buttando i miei due centesimi in ...

All'interno del mio dbConfiguration.cs, mi piace avvolgere il mio metodo context.SaveChanges () in un try / catch e produrre un file di testo di output che mi permetta di leggere chiaramente gli errori, e questo codice li timestamp - pratici se incorrere in più di un errore in tempi diversi!

        try
        {
            context.SaveChanges();
        }
        catch (DbEntityValidationException e)
        {
            //Create empty list to capture Validation error(s)
            var outputLines = new List<string>();

            foreach (var eve in e.EntityValidationErrors)
            {
                outputLines.Add(
                    $"{DateTime.Now}: Entity of type \"{eve.Entry.Entity.GetType().Name}\" in state \"{eve.Entry.State}\" has the following validation errors:");
                outputLines.AddRange(eve.ValidationErrors.Select(ve =>
                    $"- Property: \"{ve.PropertyName}\", Error: \"{ve.ErrorMessage}\""));
            }
            //Write to external file
            File.AppendAllLines(@"c:\temp\dbErrors.txt", outputLines);
            throw;
        }

basta controllare la lunghezza del campo della tabella del database. Il testo di input è maggiore della lunghezza della lunghezza del tipo di dati del campo colonna







entity-framework-4.1