DbEntityValidationException - How can I easily tell what caused the error?

I have a project that uses Entity Framework. While calling SaveChanges on my DbContext, I get the following exception:

System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

This is all fine and dandy, but I don't want to attach a debugger every time this exception occurs. More over, in production environments I cannot easily attach a debugger so I have to go to great lengths to reproduce these errors.

How can I see the details hidden within the DbEntityValidationException?


Solution 1:

The easiest solution is to override SaveChanges on your entities class. You can catch the DbEntityValidationException, unwrap the actual errors and create a new DbEntityValidationException with the improved message.

  1. Create a partial class next to your SomethingSomething.Context.cs file.
  2. Use the code at the bottom of this post.
  3. That's it. Your implementation will automatically use the overriden SaveChanges without any refactor work.

Your exception message will now look like this:

System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details. The validation errors are: The field PhoneNumber must be a string or array type with a maximum length of '12'; The LastName field is required.

You can drop the overridden SaveChanges in any class that inherits from 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);
        }
    }
}

The DbEntityValidationException also contains the entities that caused the validation errors. So if you require even more information, you can change the above code to output information about these entities.

See also: http://devillers.nl/improving-dbentityvalidationexception/

Solution 2:

As Martin indicated, there is more information in the DbEntityValidationResult. I found it useful to get both my POCO class name and property name in each message, and wanted to avoid having to write custom ErrorMessage attributes on all my [Required] tags just for this.

The following tweak to Martin's code took care of these details for me:

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

Solution 3:

To view the EntityValidationErrors collection, add the following Watch expression to the Watch window.

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

I'm using visual studio 2013

Solution 4:

While you are in debug mode within the catch {...} block open up the "QuickWatch" window (ctrl+alt+q) and paste in there:

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

This will allow you to drill down into the ValidationErrors tree. It's the easiest way I've found to get instant insight into these errors.

For Visual 2012+ users who care only about the first error and might not have a catch block, you can even do:

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