Easy way to improve DbEntityValidationException of Entity Framework

Door Martin Devillers Op 04 March 2013 Met Reacties

DbEntityValidationException is the exception thrown by Entity Framework when entity validation fails. While this exception is extremely valuable, the exception message omits the most important bit of information: The actual validation errors. In this blog, I present a quick and easy way to unwrap and rethrow these exceptions with a more meaningful message.

Developers who have used Entity Framework are probably familiar with the following message:

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

This message is quite clear in its intent: Something isn’t valid and if you want to find out what that is then you should attach a debugger and inspect the exception. While this is certainly a valid approach, attaching debuggers isn’t always practical (e.g. production environments). Moreover, being the lazy developer I am, I don’t want to attach a debugger to find out what field caused the error: I want the exception message to tell me in the first place!

A bit of (db)context please

DbEntityValidationException is thrown by the SaveChanges method which resides on the DbContext class. Fortunately, you can override the default behavior of SaveChanges. Assuming you use a database-first approach, your Visual Studio solution should contain a bunch of files like these:

  • Northwind.cs
  • Northwind.Context.cs
  • Northwind.Context.tt
  • Northwind.Designer.cs
  • Northwind.edmx
  • Northwind.edmx.diagram
  • Northwind.tt

Northwind.Context.cs is the file of interest here. This file is automatically generated by Northwind.Context.tt. It describes the class you use in code to perform operations on the database. This class inherits from DbContext, which in turn contains SaveChanges, the method responsible for throwing the DbEntityValidationException. This method is virtual and can thus be overridden. However, changing Northwind.Context.cs is not recommended since the tt-file will overwrite this file on any change to the EDMX. Luckily, the class itself is partial, which means you can add your own class to the solution.

Two steps to victory

  1. First, create a new partial class in the same location as where your EDMX resides. In my example, I called the file NorthwindEntities.cs, which is also the name of the class.
  2. Second, use the following piece of C# code to override the SaveChanges implementation.
public partial class NorthwindEntities
{
    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);
        }
    }
}

That’s it! The rest of your code will automatically use the overridden SaveChanges so you don’t have to change anything else. From now on, your exceptions will 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.

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.

I hope this blog saves you time debugging, so you can spend more time writing awesome code instead! :-)