Проверка не удалась для одного или нескольких объектов.См. Свойство EntityValidationErrors для более подробной информации. - PullRequest
742 голосов
/ 17 октября 2011

У меня возникла эта ошибка при заполнении базы данных с первым подходом кода.

Проверка не удалась для одного или нескольких объектов. См. Свойство EntityValidationErrors для более подробной информации.

Если честно, я не знаю, как проверить содержание ошибок проверки. Visual Studio показывает, что это массив с 8 объектами, поэтому 8 ошибок валидации.

Это работало с моей предыдущей моделью, но я внес несколько изменений, которые я объясню ниже:

  • У меня был enum с именем Status, я изменил его на класс с именем Status
  • Я изменил класс ApplicantsPositionHistory, чтобы иметь 2 внешних ключа для одной таблицы

Простите за длинный код, но я должен все это вставить. Исключение выдается в последней строке следующего кода.

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

Ответы [ 28 ]

1164 голосов
/ 17 октября 2011

Если честно, я не знаю, как проверить содержание ошибок проверки. Visual Studio показывает, что это массив с 8 объектами, поэтому 8 ошибок валидации.

На самом деле вы должны увидеть ошибки, если во время отладки вы углубитесь в этот массив в Visual studio. Но вы также можете перехватить исключение и записать ошибки в какое-либо хранилище журналов или консоль:

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 - это коллекция, представляющая сущности, которые не могут быть успешно проверены, а внутренняя коллекция ValidationErrors для каждой сущности - это список ошибок на уровне свойства.

Эти проверочные сообщения обычно достаточно полезны, чтобы найти источник проблемы.

Редактировать

Несколько небольших улучшений:

Значение свойства-нарушителя может быть включено во внутренний цикл следующим образом:

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

Хотя отладка Debug.Write может быть предпочтительнее, чем Console.WriteLine, поскольку она работает во всех видах приложений, а не только в консольных приложениях (спасибо @Bart за его примечание в комментариях ниже).

Для веб-приложений, работающих и использующих Elmah для регистрации исключений, для меня оказалось очень полезным создать пользовательское исключение и перезаписать SaveChanges, чтобы вызвать это новое исключение.

Тип пользовательского исключения выглядит следующим образом:

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

И SaveChanges могут быть перезаписаны следующим образом:

public class MyContext : DbContext
{
    // ...

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

Несколько замечаний:

  • Желтый экран ошибки, который Elmah показывает в веб-интерфейсе или в отправленных электронных письмах (если вы это настроили), теперь отображает сведения о проверке непосредственно в верхней части сообщения.

  • Перезапись свойства Message в пользовательском исключении вместо перезаписи ToString() имеет то преимущество, что стандартный ASP.NET «Желтый экран смерти (YSOD)» также отображает это сообщение. В отличие от Эльмы, YSOD, очевидно, не использует ToString(), но оба отображают свойство Message.

  • Оборачивание оригинальной DbEntityValidationException как внутреннего исключения гарантирует, что исходная трассировка стека все еще будет доступна и будет отображаться в Elmah и YSOD.

  • Устанавливая точку останова на строке throw newException;, вы можете просто просмотреть свойство newException.Message в виде текста, а не углубляться в проверочные коллекции, что немного неудобно и, похоже, не так легко для всех (см. комментарии ниже).

426 голосов
/ 03 июля 2014

Вы можете сделать это из Visual Studio во время отладки без написания кода, даже блока перехвата.

Просто добавьте часы с именем:

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

Выражение наблюдения $exception отображает любое исключение, выданное в текущем контексте, даже если оно не было перехвачено и присвоено переменной.

На основе http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/

95 голосов
/ 25 марта 2014

Это может сделать это без необходимости писать код:

В блоке catch добавьте точку останова в следующей строке кода:

catch (Exception exception)
{

}

Теперь, если вы наведите курсор на exception или добавите его к Watch, а затем перейдите к деталям исключения, как показано ниже; вы увидите, какие столбцы (столбцы) вызывают / вызывают проблему, поскольку эта ошибка обычно возникает при нарушении ограничения таблицы.

enter image description here

Большое изображение

44 голосов
/ 22 ноября 2016

Вот как вы можете проверить содержимое EntityValidationErrors в Visual Studio (без написания дополнительного кода), т.е. во время Отладка в IDE .

Проблема?

Вы правы, отладчик Visual Studio Просмотр сведений Во всплывающем окне не отображаются фактические ошибки в коллекции EntityValidationErrors.

enter image description here

Решение!

Просто добавьте следующее выражение в окно Quick Watch и нажмите Переоценить .

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

В моем случае посмотрите, как я могу расшириться до ValidationErrors List внутри EntityValidationErrors коллекции

enter image description here

Ссылки: mattrandle.me сообщение в блоге , @ yoel's answer

37 голосов
/ 17 сентября 2014

Чтобы быстро увидеть первую ошибку, даже не добавив часы, вы можете вставить ее в окно «Немедленно»:

((System.Data.Entity.Validation.DbEntityValidationException)$exception)
    .EntityValidationErrors.First()
    .ValidationErrors.First()
14 голосов
/ 21 октября 2013

Для тех, кто работает в 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
11 голосов
/ 27 августа 2014

Пока вы находитесь в режиме отладки в блоке catch {...}, откройте окно «QuickWatch» ( ctrl + alt + q ) и вставьте в есть:

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

или

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

Если вы не участвуете в программе try / catch или не имеете доступа к объекту исключения.

Это позволит вам углубиться в дерево ValidationErrors. Это самый простой способ мгновенно понять эти ошибки.

10 голосов
/ 12 июня 2014

Если вы просто перехватываете общее исключение, вам может быть выгодно использовать его как DbEntityValidationException .Этот тип исключения имеет свойство «Ошибки проверки», и, продолжая расширять свой путь к ним, вы найдете все проблемы.

Например, если вы поставили точку останова в улове, вы можете броситьследуя в часы:

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

Пример ошибки: , если поле не допускает нулевые значения, и у вас есть нулевая строка, вы увидите, что это поле является обязательным.

9 голосов
/ 08 февраля 2016

В режиме отладки вы можете ввести это в поле ввода оценщика выражений QuickWatch:

context.GetValidationErrors()
8 голосов
/ 09 марта 2015

просто проверьте длину поля таблицы базы данных.Ваш входной текст больше, чем длина типа данных поля столбца длина

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...