ChatGPT解决这个技术问题 Extra ChatGPT

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

我有一个使用实体框架的项目。在我的 DbContext 上调用 SaveChanges 时,我收到以下异常:

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

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

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


L
Leniel Maccaferri

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

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

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

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

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

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/


生成的 Entities 类已经从 DbContext 继承,因此您不必在分部类上再次添加它。通过将其添加到部分类中,您不会破坏或更改任何内容。事实上,如果你添加从 DbContext 的继承,Resharper 会建议你删除它:“基本类型 'DbContext' 已在其他部分指定。”
为什么这不是 SaveChanges 的默认行为?
“为什么这不是 SaveChanges 的默认行为?” - 这是一个非常好的问题。这是一个了不起的解决方案,它为我节省了几个小时!我不得不投入using System.Linq;
我在 try 块中的 base.SaveChanges() 上的 Create View 错误。它永远不会跳入 catch 块。我让您的代码覆盖了 SaveChanges,但它永远不会在错误时进入 Catch 块。
您应该设置内部异常以保留堆栈跟踪。
E
Eric Hirst

正如 Martin 所指出的,DbEntityValidationResult 中有更多信息。我发现在每条消息中获取我的 POCO 类名称和属性名称很有用,并且希望避免为此在我的所有 [Required] 标记上编写自定义 ErrorMessage 属性。

以下对 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);
    }
}

Daring Codersgithub 中使用 SelectMany and Aggregate
L
Luis

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

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

我正在使用视觉工作室 2013


$exception 太棒了!这意味着在即时窗口中我可以执行 $exception.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage);
G
GONeale

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

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

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

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

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

C
Chris Halcrow

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

添加快速监视: ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors

像这样深入到 EntityValidationErrors:(集合项,例如 [0])> ValidationErrors >(集合项,例如 [0])> ErrorMessage


C
Calvin

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


L
Luis Toapanta

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

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

保留原始堆栈跟踪

遵循开放/封闭原则(即:我可以为不同类型的日志使用不同的消息)

在生产环境中,可能会在其他地方(即:其他 dbcontext)抛出 DbEntityValidationException。


J
Juri

对于 Azure Functions,我们使用 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);
}

A
Atta H.

在您的代码中使用 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;
}

您也可以在此处查看详细信息

http://mattrandle.me/viewing-entityvalidationerrors-in-visual-studio/ 一个或多个实体的验证失败。有关更多详细信息,请参阅“EntityValidationErrors”属性 http://blogs.infosupport.com/improving-dbentityvalidationexception/


您的第三个链接是已接受答案博客的副本,但位于不同的站点上。第二个链接是一个堆栈溢出问题,它已经引用了您的第一个链接。
那么,试图帮助有适当参考的人在这里有什么问题吗?
是的,您的答案不应仅包含链接。做一个回答问题的摘要,然后在最后发布链接以供进一步阅读。