DbEntityValidationException-如何轻松分辨出导致错误的原因?


217

我有一个使用实体框架的项目。在调用SaveChangesmy时DbContext,出现以下异常:

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

这一切都很好,但我不想每次发生此异常时都附加一个调试器。而且,在生产环境中,我无法轻松地连接调试器,因此我必须竭尽全力来重现这些错误。

如何查看隐藏在其中的详细信息DbEntityValidationException

Answers:


429

最简单的解决方案是SaveChanges在实体类上重写。您可以捕获DbEntityValidationException,解开实际的错误并DbEntityValidationException使用改进后的消息创建新的错误。

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

您的异常消息现在将如下所示:

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

您可以将重写的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);
        }
    }
}

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

另请参阅:http : //devillers.nl/improving-dbentityvalidationexception/


6
生成的Entities类已经从DbContext继承了,因此您不必在分部类上再次添加它。通过将其添加到部分类中,您不会破坏或更改任何内容。实际上,如果您从DbContext添加继承,Resharper将建议您删除它:“其他类型已经指定了基本类型'DbContext'。”
Martin Devillers

15
为什么这不是SaveChanges的默认行为?
John Shedletsky 2015年

4
“为什么这不是SaveChanges的默认行为?” -这是一个很好的问题。这是一个了不起的解决方案,它节省了我几个小时!我不得不扔掉using System.Linq;
约翰·约翰·

1
我的Create View错误在try块中的base.SaveChanges()上。它永远不会跳入catch块。我让您的代码克服了SaveChanges的问题,但是它永远不会由于错误而进入Catch块。
JustJohn

7
您应该设置内部异常以保留堆栈跟踪。
dotjoe

48

正如Martin所指出的,中有更多信息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);
    }
}

1
Daring CodersSelectMany and Aggregategithub中使用
Kiquenet

43

要查看EntityValidationErrors集合,请在“监视”窗口中添加以下“监视”表达式。

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

我正在使用Visual Studio 2013


$ except是很棒的!这意味着在立即窗口中,我可以执行$ exception.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage);
chrispepper1989 '12

13

catch {...}块中处于调试模式时,打开“ QuickWatch”窗口(ctrl+ alt+ q)并粘贴在其​​中:

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

这样您就可以深入了解 ValidationErrors树中。这是我发现的立即了解这些错误的最简单方法。

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

((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors.First().ValidationErrors.First().ErrorMessage

9

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

  • 快速查看以下内容:

    ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
  • 像这样深入研究EntityValidationErrors:

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


5

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


4

我认为“实际的验证错误”可能包含敏感信息,这可能就是Microsoft选择将其放置在其他位置(属性)的原因。此处标记的解决方案是可行的,但应谨慎使用。

我希望创建一个扩展方法。这样做的更多原因:

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

1

对于Azure函数,我们使用对Microsoft.Extensions.Logging.ILogger的简单扩展

public static class LoggerExtensions
{
    public static void Error(this ILogger logger, string message, Exception exception)
    {
        if (exception is DbEntityValidationException dbException)
        {
            message += "\nValidation Errors: ";
            foreach (var error in dbException.EntityValidationErrors.SelectMany(entity => entity.ValidationErrors))
            {
                message += $"\n * Field name: {error.PropertyName}, Error message: {error.ErrorMessage}";
            }
        }

        logger.LogError(default(EventId), exception, message);
    }
}

和示例用法:

try
{
    do something with request and EF
}
catch (Exception e)
{
    log.Error($"Failed to create customer due to an exception: {e.Message}", e);
    return await StringResponseUtil.CreateResponse(HttpStatusCode.InternalServerError, e.Message);
}

0

在代码中使用try块,例如

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

您也可以在这里查看详细信息

  1. http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/

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

  3. http://blogs.infosupport.com/improving-dbentityvalidationexception/


您的第三个链接是已接受答案的博客的副本,但位于其他站点上。第二个链接是一个堆栈溢出问题,已经引用了您的第一个链接。
Eris)

因此,尝试帮助具有适当参考资料的人在这里有什么问题?
Atta H.

是的,您的答案不应仅包含链接。做出总结以回答问题,然后在最后发布链接以供进一步阅读。
ChrisO 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.