一个或多个实体的验证失败。 有关更多详细信息,请参阅“EntityValidationErrors”属性
使用代码优先的方法播种我的数据库时出现此错误。
一个或多个实体的验证失败。 有关更多详细信息,请参阅“EntityValidationErrors”属性。
说实话我不知道如何检查验证错误的内容,Visual Studio向我展示了它有8个对象的数组。 所以8验证错误。
这是与我以前的模型,但我做了一些改变,我下面解释:
- 我有一个名为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 } } }
说实话,我不知道如何检查验证错误的内容,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
实体,每个实体的内部集合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进行异常日志记录的Web应用程序来说,创建一个自定义的异常并覆盖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
属性。 -
将原始
DbEntityValidationException
包装为内部异常,确保原始堆栈跟踪仍然可用,并在Elmah和YSOD中显示。 -
通过在行上设置断点
throw newException;
您可以简单地将newException.Message
属性作为文本进行检查,而不是钻取到验证集合中,这有点尴尬,而且对于每个人来说都不是很容易(见下面的注释)。
您可以在调试期间从Visual Studio中完成,无需编写任何代码,甚至不需要catch块。
只需添加一个名字为:
((System.Data.Entity.Validation.DbEntityValidationException)$除外).EntityValidationErrors
监视表达式$exception
显示当前上下文中抛出的任何异常,即使它没有被捕获并赋值给一个变量。
基于http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/
这实际上可以做到这一点,而不必编写代码:
在你的catch块中,在下面这行代码处添加一个断点:
catch (Exception exception) { }
现在,如果您将鼠标悬停在“ exception
或将其添加到“ Watch
,然后导航到如下所示的例外详细信息; 你会看到哪个特定的列正在导致问题,因为这个错误通常在违反表约束时发生。
大图像
要快速查看第一个错误而不添加手表,可以将其粘贴到即时窗口中:
((System.Data.Entity.Validation.DbEntityValidationException)$exception) .EntityValidationErrors.First() .ValidationErrors.First()
以下是如何在Visual Studio中检查EntityValidationErrors的内容(无需编写任何额外的代码),即在IDE中调试期间。
问题?
你是对的,Visual Studio调试器的View Details Popup不会在EntityValidationErrors
集合中显示实际的错误。
解决方案!
只需在“ 快速监视”窗口中添加以下表达式,然后单击“ 重新评估” 。
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
在我的情况下,看看我能够扩展到EntityValidationErrors
集合中的ValidationErrors
List
参考文献: mattrandle.me博客文章 , @ yoel的答案
对于任何在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
当您在catch {...}
块内进入调试模式时,打开“QuickWatch”窗口( ctrl + alt + q )并粘贴到那里:
((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
要么:
((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
如果您不在try / catch中或者无法访问异常对象。
这将允许您深入查看ValidationErrors
树。 这是我发现能够即时洞察这些错误的最简单的方法。
如果您只是捕获一个泛型异常,则可能会将其作为DbEntityValidationException进行强制转换 。 这种类型的异常有一个验证错误属性,并继续扩大你的方式,你会发现所有的问题。
例如,如果您在捕捉中放置了一个断点,则可以将以下内容放入一个表中:
((System.Data.Entity.Validation.DbEntityValidationException ) ex)
一个错误的例子是, 如果一个字段不允许空值,并且你有一个空字符串,你会看到它说该字段是必需的。
在调试中,您可以在QuickWatch表达式求值器输入字段中输入:
context.GetValidationErrors()
请注意, Entity.GetType().BaseType.Name
给出您指定的类型名称,而不是其名称中的所有十六进制数字。
使用@Slauma的答案,我已经做了一个代码片段(一个包围片段)更好地使用。
<?xml version="1.0" encoding="utf-8"?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <SnippetTypes> <SnippetType>SurroundsWith</SnippetType> </SnippetTypes> <Title>ValidationErrorsTryCatch</Title> <Author>Phoenix</Author> <Description> </Description> <HelpUrl> </HelpUrl> <Shortcut> </Shortcut> </Header> <Snippet> <Code Language="csharp"><![CDATA[try { $selected$ $end$ } catch (System.Data.Entity.Validation.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; }]]></Code> </Snippet> </CodeSnippet> </CodeSnippets>
我不得不在立即窗口中写下这个: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]
为了深入到确切的错误!
Per @ Slauma的回答和@ Milton的建议我用一个try / catch扩展了我们的基类的自定义保存方法,它将处理(并因此登录我们的错误日志!)这些异常。
// 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); } }
在try catch中捕获异常,然后快速观察或按Ctrl + d和ctrl + q,您可以深入到EntityValidationErrors。
只是检查您的数据库表字段长度。 您的输入文本大于列字段数据类型长度的长度
@Slauma的答案真的很棒,但是我发现当ComplexType属性无效的时候它并不起作用。
例如,假设您有一个复杂类型PhoneNumber
的属性Phone
。 如果AreaCode
属性无效,则ve.PropertyNames
的属性名称为“Phone.AreaCode”。 这会导致调用eve.Entry.CurrentValues<object>(ve.PropertyName)
失败。
要解决这个问题,你可以在每个属性名称分割.
,然后递归通过结果数组的属性名称。 最后,当你到达链条的底部,你可以简单地返回属性的值。
下面是支持ComplexTypes的@ Slauma的FormattedDbEntityValidationException
类。
请享用!
[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()); } }
只是把我的两分钱…
在我的dbConfiguration.cs中,我喜欢将我的context.SaveChanges()方法封装到try / catch中,并生成一个输出文本文件,使我可以清楚地读取错误,并且此代码也为它们加上时间戳 – 在不同的时间遇到多个错误!
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; }
我发现什么…当我得到'EntityValidationErrors'错误是….我有一个字段在我的数据库'db1'在表'tbladdress'作为'地址1'大小为100(即地址varchar(100)空),我传递值超过100个字符..这导致错误,同时保存数据到数据库….
所以你必须检查你传递给现场的数据。
这对我有用。
var modelState = ModelState.Values; if (!ModelState.IsValid) { return RedirectToAction("Index", "Home", model); }
在if语句上放置一个断点。 然后你可以在调试窗口中检查modelState。 在每一个值你可以看到是否有错误,甚至是错误信息。 而已。 当你不再需要它时,只需删除或注释该行。
我希望这会有所帮助。
如果问,我可以在调试窗口中提供详细的屏幕截图。
正如其他文章中提到的,只需在DbEntityValidationException类中捕获异常即可。 在错误的情况下,这将会给你提供所需的信息。
try { .... } catch(DbEntityValidationException ex) { .... }
我以前遇到过这个错误
当我试图更新我的模型在实体框架中的特定领域
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; }
并根据以上的答案
我发现验证消息The SignerName field is required.
指向我的模型中的字段
当我检查我的数据库架构,我发现
所以关闭coure ValidationException
有权提出
根据这个领域,我希望它是可空的(我不知道我是如何搞砸的)
所以我改变了这个字段以允许Null,通过这个我的代码不会再给我这个错误
所以这个错误可能会发生,如果你使你的数据库的数据完整性失效