我在用代码优先方法播种数据库时遇到了这个错误。

一个或多个实体的验证失败。有关详细信息,请参阅“EntityValidationErrors”属性。

老实说,我不知道如何检查验证错误的内容。Visual Studio向我展示了它是一个包含8个对象的数组,因此有8个验证错误。

这适用于我以前的模型,但我做了一些更改,我将在下面解释:

我有一个名为Status的枚举,我将其更改为名为Status我将类ApplientsPositionHistory更改为具有同一表的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; }
    }
}

使用系统;使用System.Collections.Generic;使用System.Linq;使用System.Text;使用System.Data.Entity;使用System.IO;命名空间Data.Model{公共类HRContextInitializer:DropCreateDatabaseAlways<HRContext>{受保护的覆盖void Seed(HRContext上下文){#地区状态已应用状态=新状态(){Status=“已应用”};状态reviewedByHR=新状态(){Status=“人力资源审核”};状态approvedByHR=新状态(){Status=“已由HR批准”};Status rejectedByHR=新状态(){Status=“被HR拒绝”};分配给技术部门的状态=新状态(){Status=“分配给技术部”};技术部门批准的状态=新状态(){Status=“技术部门批准”};技术部门拒绝的状态=新状态(){Status=“技术部门拒绝”};Status assignedToGeneral Manager=新状态(){Status=“分配给General Manager”};状态approvedByGeneralManager=新状态(){Status=“已由总经理批准”};Status rejectedByGeneralManager=新状态(){Status=“被总经理拒绝”};context.Status.Add(已应用);context.Status.Add(由HR审查);context.Status.Add(已由HR批准);context.Status.Add(被HR拒绝);context.Status.Add(分配给技术部门);context.Status.Add(由技术部门批准);context.Status.Add(被技术部门拒绝);context.Status.Add(分配给GeneralManager);context.Status.Add(由GeneralManager批准);context.Status.Add(被GeneralManager拒绝);#末端区域#区域位置职位netdeveloper=new职位(){name=“.net developer”,所需年经验=5};职位javadeveloper=new Position(){name=“java developer”,yearsExperienceRequired=5};context.Positions.Add(netdeveloper);context.Positions.Add(javadeveloper);#末端区域#地区申请人申请人luis=新申请人(){name=“Luis”,skypeuser=“le.valencia”,电话=“0491732825”,photo=文件.ReadAllBytes(@“C:\Users\LUIS.SIMBIOS\Documents\Visual Studio 2010\Projects\SlnHR\HRRazorForms\Content\pictures\1.jpg”)};申请人john=新申请人(){name=“John”,skypeuser=“jo.valencia”,电话=“3435343543”,photo=文件.ReadAllBytes(@“C:\Users\LUIS.SIMBIOS\Documents\Visual Studio 2010\Projects\SlnHR\HRRazorForms\Content\pictures\2.jpg”)};context.申请人.添加(luis);context.申请人.Add(john);#末端区域#区域应用程序位置ApplicantPosition appicantposition=新的ApplicantPosition(){申请人=luis,位置=网络开发者,appliedDate=日期时间。今天,状态ID=1};ApplicantPosition appicantposition2=新的ApplicantPosition(){申请人=john,位置=javadeveloper,appliedDate=日期时间。今天,状态ID=1}; context.ApplicantsPosition.Add(applicantposition);context.ApplicantsPositions.Add(applicantposition2);#末端区域context.SaveChanges();--->>此处出错}}}


当前回答

把我的两分钱扔进去。。。

在我的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;
        }

其他回答

这对我有用。

var modelState = ModelState.Values;
if (!ModelState.IsValid)
{
    return RedirectToAction("Index", "Home", model);
}

在if语句上设置断点。然后可以在调试窗口中检查modelState。在每个值上,您都可以看到是否存在错误,甚至错误消息。就是这样。当您不再需要它时,只需删除或注释该行即可。

我希望这会有所帮助。

如果需要,我可以在调试窗口中提供详细的屏幕截图。

根据@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);
    }
}

我以前遇到过这个错误

当我试图在实体框架中更新模型中的特定字段时

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

根据上述答案

我发现验证消息需要SignerName字段。

指向我模型中的字段

当我检查数据库模式时,我发现

因此场外ValidationException有权引发

根据这个字段,我希望它可以为空,(我不知道我是怎么搞砸的)

所以我将该字段更改为允许Null,这样我的代码就不会再出现此错误

因此,如果您使数据库的数据完整性无效,可能会发生此错误

@Slauma的回答非常好,但我发现当ComplexType属性无效时,它不起作用。

例如,假设您有一个复杂类型PhoneNumber的属性Phone。如果AreaCode属性无效,则ve.PropertyNames中的属性名称为“Phone.AreaCode”。这将导致对eve.Entry.CurrentValues<object>(ve.PropertyName)的调用失败。

要解决此问题,可以在每个位置拆分属性名称。,然后递归遍历生成的属性名称数组。最后,当您到达链的底部时,只需返回属性的值。

下面是@Slauma的FormattedDbEntityValidationException类,支持ComplexTypes。

享受

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

老实说,我不知道如何检查验证错误的内容。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是表示无法成功验证的实体的集合,每个实体的内部集合ValidationError是属性级别的错误列表。

这些验证消息通常有助于找到问题的根源。

Edit

一些轻微的改进:

违规属性的值可以包含在内部循环中,如下所示:

        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中。通过在抛出newException的行上设置断点;您可以简单地将newException.Message属性作为文本进行检查,而不是深入验证集合,这有点尴尬,似乎对每个人都不容易(见下面的注释)。