使用代码优先方法为数据库播种时出现此错误。
一个或多个实体的验证失败。有关更多详细信息,请参见’EntityValidationErrors’属性。
老实说,我不知道如何检查验证错误的内容。Visual Studio向我展示了这是一个包含8个对象的数组,因此出现8个验证错误。
这适用于我以前的模型,但是做了一些更改,下面解释:
对冗长的代码,请原谅,但我必须全部粘贴。在以下代码的最后一行中引发异常。
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 } } }
实际上,如果在调试期间在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实体的集合,每个实体的内部集合是属性级别的错误列表。
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在下面的评论中提供他的注释)。
Debug.Write
Console.WriteLine
对于在生产中并且使用 Elmah 进行异常日志记录的Web应用程序,事实证明,这对于我创建自定义异常并进行覆盖SaveChanges以抛出此新异常非常有用。
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在Web界面或已发送的电子邮件(如果已配置)中显示的黄色错误屏幕现在直接在消息顶部显示验证详细信息。
Message在自定义异常中覆盖而不是覆盖属性ToString()的好处是,标准ASP.NET“死亡黄屏(YSOD)”也显示此消息。与Elmah相反,YSOD显然不使用ToString(),但是两者都显示Message属性。
Message
ToString()
将原始文件包装DbEntityValidationException为内部异常可确保原始堆栈跟踪仍然可用,并显示在Elmah和YSOD中。
DbEntityValidationException
通过在行上设置一个断点,throw newException;您可以简单地将newException.Message属性作为文本进行检查,而不必钻研验证集合,这有点尴尬,而且似乎并不适合所有人(请参见下面的评论)。
throw newException;
newException.Message