DbEntityValidationException – ¿Cómo puedo saber fácilmente qué causó el error?

Tengo un proyecto que usa Entity Framework. Al llamar a SaveChanges en mi DbContext , obtengo la siguiente excepción:

System.Data.Entity.Validation.DbEntityValidationException: la validación falló para una o más entidades. Consulte la propiedad ‘EntityValidationErrors’ para más detalles.

Todo esto está bien y muy bien, pero no quiero adjuntar un depurador cada vez que se produce esta excepción. Además, en entornos de producción no puedo adjuntar fácilmente un depurador, así que tengo que hacer todo lo posible para reproducir estos errores.

¿Cómo puedo ver los detalles ocultos dentro de DbEntityValidationException ?

La solución más fácil es anular SaveChanges en su clase de entidades. Puede capturar la DbEntityValidationException , desenvolver los errores reales y crear una nueva DbEntityValidationException con el mensaje mejorado.

  1. Crea una clase parcial al lado de tu archivo SomethingSomething.Context.cs.
  2. Usa el código en la parte inferior de esta publicación.
  3. Eso es. Su implementación utilizará automáticamente los SaveChanges modificados sin ningún trabajo de refactorización.

Su mensaje de excepción ahora se verá así:

System.Data.Entity.Validation.DbEntityValidationException: la validación falló para una o más entidades. Consulte la propiedad ‘EntityValidationErrors’ para más detalles. Los errores de validación son: El campo PhoneNumber debe ser una cadena o tipo de matriz con una longitud máxima de ’12’; El campo Apellido es obligatorio.

Puede descartar SaveChanges anulado en cualquier clase que herede de 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); } } } 

La DbEntityValidationException también contiene las entidades que causaron los errores de validación. Entonces, si necesita aún más información, puede cambiar el código anterior para generar información sobre estas entidades.

Ver también: http://devillers.nl/improving-dbentityvalidationexception/

Como indicó Martin, hay más información en DbEntityValidationResult . Me resultó útil obtener mi nombre de clase y nombre de propiedad de POCO en cada mensaje, y quería evitar tener que escribir atributos personalizados de ErrorMessage en todas mis tags [Required] solo para esto.

El siguiente ajuste al código de Martin se encargó de estos detalles para mí:

 // Retrieve the error messages as a list of strings. List errorMessages = new List(); 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); } } 

Para ver la colección EntityValidationErrors , agregue la siguiente expresión Watch a la ventana Watch.

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

Estoy usando Visual Studio 2013

Mientras estás en el modo de depuración dentro del bloque catch {...} abre la ventana “QuickWatch” ( ctrl + alt + q ) y pega allí:

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

Esto le permitirá profundizar en el árbol ValidationErrors . Es la forma más fácil que he encontrado para obtener una visión instantánea de estos errores.

Para usuarios de Visual 2012+ que solo se preocupan por el primer error y que quizás no tengan un bloque catch , incluso pueden hacer:

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

Para encontrar rápidamente un mensaje de error significativo al inspeccionar el error durante la depuración:

  • Agrega un reloj rápido para:

     ((System.Data.Entity.Validation.DbEntityValidationException)$exception).EntityValidationErrors 
  • Profundiza en EntityValidationErrors como este:

    (elemento de recostackción, por ejemplo [0])> ValidationErrors> (elemento de recostackción, por ejemplo, [0])> ErrorMessage

En realidad, este es solo el problema de validación, EF validará las propiedades de la entidad primero antes de realizar cualquier cambio en la base de datos. Entonces, EF verificará si el valor de la propiedad está fuera de rango, como cuando diseñó la tabla. Table_Column_UserName es varchar (20). Pero, en EF, ingresaste un valor mayor a 20. O, en otros casos, si la columna no permite ser un valor nulo. Por lo tanto, en el proceso de validación, debe establecer un valor para la columna que no sea nulo, sin importar si va a realizar el cambio en él. Personalmente, me gusta la respuesta de Leniel Macaferi. Puede mostrarle los detalles de los problemas de validación

Creo que “Los errores de validación reales” pueden contener información confidencial, y esta podría ser la razón por la cual Microsoft eligió ponerlos en otro lugar (propiedades). La solución marcada aquí es práctica, pero debe tomarse con precaución.

Prefiero crear un método de extensión. Más razones para esto:

  • Mantenga el rastro original de la stack
  • Siga el principio abierto / cerrado (es decir .: puedo usar diferentes mensajes para diferentes tipos de registros)
  • En entornos de producción, podría haber otros lugares (es decir, otros dbcontext) donde podría lanzarse una DbEntityValidationException.

Para Azure Functions utilizamos esta extensión simple para 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); } } 

y uso de ejemplo:

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

Usa try block en tu código como

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

Puede verificar los detalles aquí también

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

  2. La validación falló para una o más entidades. Consulte la propiedad ‘EntityValidationErrors’ para más detalles

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