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

C#Entity Framework

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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 - C#

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 - C#

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 - C#

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

Solution 5 - C#

To quickly find a meaningful error message by inspecting the error during debugging:

  • Add a quick watch for:

     ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors
    
  • Drill down into EntityValidationErrors like this:

(collection item e.g. [0]) > ValidationErrors > (collection item e.g. [0]) > ErrorMessage

Solution 6 - C#

Actually, this is just the validation issue, EF will validate the entity properties first before making any changes to the database. So, EF will check whether the property's value is out of range, like when you designed the table. Table_Column_UserName is varchar(20). But, in EF, you entered a value that longer than 20. Or, in other cases, if the column does not allow to be a Null. So, in the validation process, you have to set a value to the not null column, no matter whether you are going to make the change on it. I personally, like the Leniel Macaferi answer. It can show you the detail of the validation issues

Solution 7 - C#

I think "The actual validation errors" may contain sensitive information, and this could be the reason why Microsoft chose to put them in another place (properties). The solution marked here is practical, but it should be taken with caution.

I would prefer to create an extension method. More reasons to this:

  • Keep original stack trace
  • Follow open/closed principle (ie.: I can use different messages for different kind of logs)
  • In production environments there could be other places (ie.: other dbcontext) where a DbEntityValidationException could be thrown.

Solution 8 - C#

For Azure Functions we use this simple extension to 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);
    }
}

and example usage:

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

Solution 9 - C#

Use try block in your code like

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

You can check the details here as well

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

  2. https://stackoverflow.com/questions/7795300/validation-failed-for-one-or-more-entities-see-entityvalidationerrors-propert

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMartin DevillersView Question on Stackoverflow
Solution 1 - C#Martin DevillersView Answer on Stackoverflow
Solution 2 - C#Eric HirstView Answer on Stackoverflow
Solution 3 - C#Shehab FawzyView Answer on Stackoverflow
Solution 4 - C#GONealeView Answer on Stackoverflow
Solution 5 - C#Chris HalcrowView Answer on Stackoverflow
Solution 6 - C#CalvinView Answer on Stackoverflow
Solution 7 - C#Luis ToapantaView Answer on Stackoverflow
Solution 8 - C#JuriView Answer on Stackoverflow
Solution 9 - C#Atta H.View Answer on Stackoverflow