DbEntityValidationException—我如何能很容易地知道是什么导致了错误

本文关键字:是什么 错误 何能 很容易 DbEntityValidationException | 更新日期: 2023-09-27 18:07:31

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

System.Data.Entity.Validation。DbEntityValidationException:验证一个或多个实体失败。参见'EntityValidationErrors'属性查看详细信息。

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

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

DbEntityValidationException—我如何能很容易地知道是什么导致了错误

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

  1. 在你的SomethingSomething.Context.cs文件旁边创建一个局部类
  2. 使用本文底部的代码。
  3. 就是这样。您的实现将自动使用重写的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/

正如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);
    }
}

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

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

我使用visual studio 2013

当您在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

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

  • 添加快速监视:

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

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

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

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

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

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

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

在代码中使用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/
  1. 一个或多个实体验证失败。看到& # 39;EntityValidationErrors& # 39;属性查看更多详细信息

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