DbEntityValidationException - 如何轻松判断导致错误的原因?

Mar*_*ers 216 c# entity-framework

我有一个使用Entity Framework的项目.在调用SaveChangesmy时DbContext,我得到以下异常:

System.Data.Entity.Validation.DbEntityValidationException:一个或多个实体的验证失败.有关详细信息,请参阅"EntityValidationErrors"属性.

这一切都很好,但每次发生此异常时我都不想附加调试器.此外,在生产环境中,我无法轻松附加调试器,因此我不得不竭尽全力重现这些错误.

我怎样才能看到隐藏在其中的细节DbEntityValidationException

Mar*_*ers 426

最简单的解决方案是覆盖SaveChanges您的实体类.您可以捕获DbEntityValidationException,解开实际错误并DbEntityValidationException使用改进的消息创建新的错误.

  1. 在SomethingSomething.Context.cs文件旁边创建一个分部类.
  2. 使用此帖子底部的代码.
  3. 而已.您的实现将自动使用覆盖的SaveChanges而无需任何重构工作.

您的异常消息现在看起来像这样:

System.Data.Entity.Validation.DbEntityValidationException:一个或多个实体的验证失败.有关详细信息,请参阅"EntityValidationErrors"属性.验证错误是:字段PhoneNumber必须是字符串或数组类型,最大长度为"12"; LastName字段是必需的.

您可以在任何继承自的类中删除重写的SaveChanges DbContext:

public partial class SomethingSomethingEntities
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            // Retrieve the error messages as a list of strings.
            var errorMessages = ex.EntityValidationErrors
                    .SelectMany(x => x.ValidationErrors)
                    .Select(x => x.ErrorMessage);
    
            // Join the list to a single string.
            var fullErrorMessage = string.Join("; ", errorMessages);
    
            // Combine the original exception message with the new one.
            var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
    
            // Throw a new DbEntityValidationException with the improved exception message.
            throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

DbEntityValidationException还包含导致验证错误的实体.因此,如果您需要更多信息,可以更改上述代码以输出有关这些实体的信息.

另见:http://devillers.nl/improving-dbentityvalidationexception/

  • 为什么这不是SaveChanges的默认行为? (14认同)
  • 您应该设置内部异常以保留堆栈跟踪. (7认同)
  • 生成的Entities类已经从DbContext继承,因此您不必在分部类上再次添加它.您不会通过将其添加到分部类来中断或更改任何内容.实际上,如果你从DbContext添加继承,Resharper会建议你删除它:"Base type'DbContext'已在其他部分中被指定." (6认同)
  • "为什么这不是SaveChanges的默认行为?" - 这是一个非常好的问题.这是一个惊人的解决方案,它节省了我几个小时!我不得不使用System.Linq;` (4认同)

Eri*_*rst 47

正如马丁所说,有更多的信息DbEntityValidationResult.我发现在每条消息中同时获取我的POCO类名和属性名是很有用的,并且希望避免必须为ErrorMessage我的所有[Required]标记写自定义属性.

以下对Martin的代码的调整为我处理了这些细节:

// Retrieve the error messages as a list of strings.
List<string> errorMessages = new List<string>();
foreach (DbEntityValidationResult validationResult in ex.EntityValidationErrors)
{
    string entityName = validationResult.Entry.Entity.GetType().Name;
    foreach (DbValidationError error in validationResult.ValidationErrors)
    {
        errorMessages.Add(entityName + "." + error.PropertyName + ": " + error.ErrorMessage);
    }
}
Run Code Online (Sandbox Code Playgroud)


She*_*wzy 42

要查看EntityValidationErrors集合,请将以下Watch表达式添加到Watch窗口.

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
Run Code Online (Sandbox Code Playgroud)

我正在使用visual studio 2013


GON*_*ale 13

当您处于catch {...}块内的调试模式时,打开"QuickWatch"窗口(ctrl+ alt+ q)并粘贴到那里:

((System.Data.Entity.Validation.DbEntityValidationException)ex).EntityValidationErrors
Run Code Online (Sandbox Code Playgroud)

这将允许您深入到ValidationErrors树中.这是我发现能够即时了解这些错误的最简单方法.

对于只关心第一个错误并且可能没有catch块的Visual 2012+用户,您甚至可以执行以下操作:

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors.First().ValidationErrors.First().ErrorMessage
Run Code Online (Sandbox Code Playgroud)


Chr*_*row 9

通过在调试期间检查错误来快速查找有意义的错误消息:

  • 添加快速监视:

    ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
    
    Run Code Online (Sandbox Code Playgroud)
  • 深入研究EntityValidationErrors,如下所示:

    (集合项例如[0])> ValidationErrors>(集合项例如[0])> ErrorMessage


小智 6

我认为“实际验证错误”可能包含敏感信息,这可能是微软选择将它们放在另一个地方(属性)的原因。这里标记的解决方案很实用,但应谨慎使用。

我更愿意创建一个扩展方法。更多原因:

  • 保留原始堆栈跟踪
  • 遵循开放/封闭原则(即:我可以对不同类型的日志使用不同的消息)
  • 在生产环境中,可能还有其他地方(即:其他 dbcontext)可能引发 DbEntityValidationException。


Cal*_*vin 5

实际上,这只是验证问题,EF将在对数据库进行任何更改之前首先验证实体属性.因此,EF会检查属性的值是否超出范围,就像设计表时一样.Table_Column_UserName是varchar(20).但是,在EF中,您输入的值大于20.或者,在其他情况下,如果列不允许为空.因此,在验证过程中,无论是否要对其进行更改,都必须将值设置为非空列.我个人,就像Leniel Macaferi的回答一样.它可以向您显示验证问题的详细信息