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

时间:2013-04-04 19:54:47

标签: c# entity-framework

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

  

System.Data.Entity.Validation.DbEntityValidationException:验证   一个或多个实体失败。请参阅'EntityValidationErrors'属性   了解更多详情。

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

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

9 个答案:

答案 0 :(得分:417)

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

  1. 在SomethingSomething.Context.cs文件旁边创建一个分部类。
  2. 使用此帖子底部的代码。
  3. 就是这样。您的实现将自动使用覆盖的SaveChanges,而无需任何重构工作。
  4. 您的异常消息现在将如下所示:

      

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

    您可以在任何继承自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/

答案 1 :(得分:47)

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

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

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

答案 2 :(得分:42)

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

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

我正在使用visual studio 2013

答案 3 :(得分:13)

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

答案 4 :(得分:9)

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

  • 添加快速监视:

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

    (收集项目,例如[0])&gt;验证错误&gt; (收集项目,例如[0])&gt;的ErrorMessage

答案 5 :(得分:5)

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

答案 6 :(得分:4)

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

我更愿意创建一个扩展方法。更多理由:

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

答案 7 :(得分:1)

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

答案 8 :(得分: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. Validation failed for one or more entities. See 'EntityValidationErrors' property for more details

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