La validación falló para una o más entidades al guardar los cambios en la base de datos de SQL Server usando Entity Framework


337

Quiero guardar mi edición en la base de datos y estoy usando Entity FrameWork Code-First en ASP.NET MVC 3 / C # pero obtengo errores. En mi clase de evento, tengo los tipos de datos DateTime y TimeSpan pero en mi base de datos, tengo fecha y hora respectivamente. Podría ser ésta la razón? ¿Cómo puedo transmitir al tipo de datos apropiado en el código antes de guardar los cambios en la base de datos?

public class Event
{
    public int EventId { get; set; }
    public int CategoryId { get; set; }
    public int PlaceId { get; set; }
    public string Title { get; set; }
    public decimal Price { get; set; }
    public DateTime EventDate { get; set; }
    public TimeSpan StartTime { get; set; }
    public TimeSpan EndTime { get; set; }
    public string Description { get; set; }
    public string EventPlaceUrl { get; set; }
    public Category Category { get; set; }
    public Place Place { get; set; }
}

Método en el controlador >>>> Problema en storeDB.SaveChanges ();

// POST: /EventManager/Edit/386        
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
    var theEvent = storeDB.Events.Find(id);

    if (TryUpdateModel(theEvent))
    {
        storeDB.SaveChanges();
        return RedirectToAction("Index");
    }
    else
    {
        ViewBag.Categories = storeDB.Categories.OrderBy(g => g.Name).ToList();
        ViewBag.Places = storeDB.Places.OrderBy(a => a.Name).ToList();
        return View(theEvent);
    }
}

con

public class EventCalendarEntities : DbContext
{
    public DbSet<Event> Events { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<Place> Places { get; set; } 
}

Base de datos SQL Server 2008 R2 / T-SQL

EventDate (Datatype = date)  
StartTime (Datatype = time)  
EndTime (Datatype = time)  

Http Form

EventDate (Datatype = DateTime) e.g. 4/8/2011 12:00:00 AM  
StartTime (Datatype = Timespan/time not sure) e.g. 08:30:00  
EndTime (Datatype = Timespan/time not sure) e.g. 09:00:00  

Error del servidor en la aplicación '/'.

La validación falló para una o más entidades. Vea la propiedad 'EntityValidationErrors' para más detalles.

Descripción: se produjo una excepción no controlada durante la ejecución de la solicitud web actual. Revise el seguimiento de la pila para obtener más información sobre el error y dónde se originó en el código.

Detalles de la excepción: System.Data.Entity.Validation.DbEntityValidationException: la validación falló para una o más entidades. Vea la propiedad 'EntityValidationErrors' para más detalles.

Error de fuente:

Line 75:             if (TryUpdateModel(theEvent))
Line 76:             {
Line 77:                 storeDB.SaveChanges();
Line 78:                 return RedirectToAction("Index");
Line 79:             }

Archivo de origen: C: \ sep \ MvcEventCalendar \ MvcEventCalendar \ Controllers \ EventManagerController.cs Línea: 77

Seguimiento de pila:

[DbEntityValidationException: error de validación para una o más entidades. Vea la propiedad 'EntityValidationErrors' para más detalles.]


8
probablemente uno de sus campos obligatorios tiene un valor nulo. Como EventDate, StartTime, Price, Category, etc.
Daveo

¿Ha inspeccionado las variables de formulario que se publican para garantizar que cada una coincida con el tipo definido de la base de datos? O como lo que dijo Daveo, falta uno de los valores de formulario requeridos ...
timothyclifford

No todas las variables de formulario publicadas coinciden con el tipo definido de la base de datos. He usado fecha y hora en la base de datos, pero no hay ningún tipo de datos directo equivalente en .NET. Por lo tanto, usé DateTime y TimeSpan. Ahora necesito convertir los dos a fecha y hora respectivamente.
user522767

El origen del error para mí fue un campo de cadena que tiene más de 30 caracteres y el tamaño de mi campo en la base de datos fue 30.
Mojtaba

Respuestas:


840

Puede extraer toda la información del DbEntityValidationExceptioncon el siguiente código (debe agregar los espacios de nombres: System.Data.Entity.Validationy System.Diagnosticsa su usinglista):

catch (DbEntityValidationException dbEx)
{
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
        foreach (var validationError in validationErrors.ValidationErrors)
        {
            Trace.TraceInformation("Property: {0} Error: {1}", 
                                    validationError.PropertyName, 
                                    validationError.ErrorMessage);
        }
    }
}

77
También puede obtener este error si algún dato inicial no satisface completamente las reglas de atributos del modelo (como Required). He agregado una respuesta con un poco más de información.
dan richardson

8
La respuesta podría mejorarse al explicar dónde puede ver realmente la salida de rastreo.
mkataja

1
Encontré que este artículo de msdn sobre trace es muy útil
Borik,

2
Usted señor, ganó internet.
Leandro

8
La salida de seguimiento se puede ver en la ventana Salida. Haga clic en Depurar en la parte superior -> Windows -> Salida
reggaeguitar

249

No se requiere cambio de código:

Mientras está en modo de depuración dentro del catch {...}bloque, abra la ventana "QuickWatch" ( Ctrl+ Alt+ Q) y pegue allí:

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

o:

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

Si no está en un try / catch o no tiene acceso al objeto de excepción.

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


66
Tú, mi amigo, eres un genio, me ayudaste a rastrear el error ET que recibía, ¡Gracias!
Mark Kram

44
No hay problema :) Pero no es un genio, simplemente ama QuickWatch :)
GONeale

44
Acabo de actualizar la respuesta para aquellos que no tienen acceso al objeto / variable de excepción.
GONeale

77
Cada vez que recibo esta excepción busco el error, luego vengo aquí (segundo resultado en Google), encuentro esta publicación y uso la segunda solución en el panel Debug> Watch. Lo hizo docenas de veces. Gracias GONeale
Ata S.

1
¿Soy el único que obtiene "El nombre '$ excepción' no existe en el contexto actual" al ejecutar la segunda consulta?
Alex Klaus

37

En el caso de que tenga clases con los mismos nombres de propiedades, aquí hay una pequeña extensión de la respuesta de Praveen:

 catch (DbEntityValidationException dbEx)
 {
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
       foreach (var validationError in validationErrors.ValidationErrors)
       {
          Trace.TraceInformation(
                "Class: {0}, Property: {1}, Error: {2}",
                validationErrors.Entry.Entity.GetType().FullName,
                validationError.PropertyName,
                validationError.ErrorMessage);
       }
    }
 }

22

Como una mejora tanto para Praveen como para Tony, uso una anulación:

public partial class MyDatabaseEntities : DbContext
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException dbEx)
        {
            foreach (var validationErrors in dbEx.EntityValidationErrors)
            {
                foreach (var validationError in validationErrors.ValidationErrors)
                {
                    Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}",
                        validationErrors.Entry.Entity.GetType().FullName,
                        validationError.PropertyName,
                        validationError.ErrorMessage);
                }
            }

            throw;  // You can also choose to handle the exception here...
        }
    }
}

6

Esta implementación ajusta la entidad de excepción a excepción con texto de detalle. Maneja DbEntityValidationException, DbUpdateException, datetime2Errores de distancia (MS SQL), e incluyen clave de la entidad válida en el mensaje (útil cuando muchas entidades savind en una SaveChangesllamada).

Primero, anule SaveChangesen la clase DbContext:

public class AppDbContext : DbContext
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException dbEntityValidationException)
        {
            throw ExceptionHelper.CreateFromEntityValidation(dbEntityValidationException);
        }
        catch (DbUpdateException dbUpdateException)
        {
            throw ExceptionHelper.CreateFromDbUpdateException(dbUpdateException);
        }
    }   

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
    {
        try
        {
            return await base.SaveChangesAsync(cancellationToken);
        }
        catch (DbEntityValidationException dbEntityValidationException)
        {
            throw ExceptionHelper.CreateFromEntityValidation(dbEntityValidationException);
        }
        catch (DbUpdateException dbUpdateException)
        {
            throw ExceptionHelper.CreateFromDbUpdateException(dbUpdateException);
        }
    }

Clase ExceptionHelper:

public class ExceptionHelper
{
    public static Exception CreateFromEntityValidation(DbEntityValidationException ex)
    {
        return new Exception(GetDbEntityValidationMessage(ex), ex);
    }

    public static string GetDbEntityValidationMessage(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);
        return exceptionMessage;
    }

    public static IEnumerable<Exception> GetInners(Exception ex)
    {
        for (Exception e = ex; e != null; e = e.InnerException)
            yield return e;
    }

    public static Exception CreateFromDbUpdateException(DbUpdateException dbUpdateException)
    {
        var inner = GetInners(dbUpdateException).Last();
        string message = "";
        int i = 1;
        foreach (var entry in dbUpdateException.Entries)
        {
            var entry1 = entry;
            var obj = entry1.CurrentValues.ToObject();
            var type = obj.GetType();
            var propertyNames = entry1.CurrentValues.PropertyNames.Where(x => inner.Message.Contains(x)).ToList();
            // check MS SQL datetime2 error
            if (inner.Message.Contains("datetime2"))
            {
                var propertyNames2 = from x in type.GetProperties()
                                        where x.PropertyType == typeof(DateTime) ||
                                            x.PropertyType == typeof(DateTime?)
                                        select x.Name;
                propertyNames.AddRange(propertyNames2);
            }

            message += "Entry " + i++ + " " + type.Name + ": " + string.Join("; ", propertyNames.Select(x =>
                string.Format("'{0}' = '{1}'", x, entry1.CurrentValues[x])));
        }
        return new Exception(message, dbUpdateException);
    }
}

Debe esperar la llamada SaveChangesAsync, si desea manejar la excepción.
Arnab Chakraborty

Gracias, Arnab Chakraborty ! Respuesta corregida
Sel

5

Este código ayudó a encontrar mi problema cuando tuve un problema con mi Entity VAlidation Erros. Me dijo el problema exacto con mi definición de entidad. Intente seguir el código donde necesita cubrir storeDB.SaveChanges (); en el siguiente intento intente bloquear.

  try
{
         if (TryUpdateModel(theEvent))
         {
             storeDB.SaveChanges();
             return RedirectToAction("Index");
         }
}
catch (System.Data.Entity.Validation.DbEntityValidationException dbEx)
{
    Exception raise = dbEx;
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
        foreach (var validationError in validationErrors.ValidationErrors)
        {
            string message = string.Format("{0}:{1}", 
                validationErrors.Entry.Entity.ToString(),
                validationError.ErrorMessage);
            // raise a new exception nesting
            // the current instance as InnerException
            raise = new InvalidOperationException(message, raise);
        }
    }
    throw raise;
}

4

Recibí este error hoy y no pude resolverlo por un tiempo, pero me di cuenta de que fue después de agregar algunos correos RequireAttributeelectrónicos a mis modelos y que algunos datos iniciales de desarrollo no poblaban todos los campos requeridos.
Solo una nota de que si obtiene este error mientras actualiza la base de datos a través de algún tipo de estrategia de inicio, DropCreateDatabaseIfModelChangesentonces debe asegurarse de que sus datos iniciales cumplan y satisfagan cualquier atributo de validación de datos del modelo .

Sé que esto es ligeramente diferente al problema en la pregunta, pero es una pregunta popular, así que pensé en agregar un poco más a la respuesta para otros que tienen el mismo problema que yo.
Espero que esto ayude a otros :)


4

Creo que agregar try / catch para cada SaveChanges()operación no es una buena práctica, es mejor centralizar esto:

Agregue esta clase a la DbContextclase principal :

public override int SaveChanges()
{
    try
    {
        return base.SaveChanges();
    }
    catch (DbEntityValidationException ex)
    {
        string errorMessages = string.Join("; ", ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage));
        throw new DbEntityValidationException(errorMessages);
    }
}

Esto sobrescribirá el SaveChanges()método de su contexto y obtendrá una lista separada por comas que contiene todos los errores de validación de la entidad.

Esto también puede mejorar, para registrar errores en el entorno de producción, en lugar de simplemente arrojar un error.

Espero que esto sea útil.


¿Dónde puedo encontrar la "Clase DbContext"? Soy un poco nuevo en todas estas cosas de MVC. Estoy usando MVC 5 y Entity Framework 6. Simplemente no sé cómo le gustaría el nombre en mi Explorador de soluciones.
JustJohn

@JustJohn está en su modelo de base de datos (archivo de clase), si no sabe dónde está, puede hacer una búsqueda completa de la palabra clave DbContexten su proyecto.
Chtiwi Malek

1
Este enfoque me gusta más, ya que es una solución universal en la aplicación y revela lo que hay debajo de la superficie fácilmente sin cambiar mucho para ningún otro módulo en el sistema
Korayem

3

Aquí hay una extensión de la extensión de Tony ... :-)

Para Entity Framework 4.x, si desea obtener el nombre y el valor del campo clave para saber qué instancia de entidad (registro DB) tiene el problema, puede agregar lo siguiente. Esto proporciona acceso a los miembros de clase ObjectContext más potentes desde su objeto DbContext.

// Get the key field name & value.
// This assumes your DbContext object is "_context", and that it is a single part key.
var e = ((IObjectContextAdapter)_context).ObjectContext.ObjectStateManager.GetObjectStateEntry(validationErrors.Entry.Entity);
string key = e.EntityKey.EntityKeyValues[0].Key;
string val = e.EntityKey.EntityKeyValues[0].Value;

3

No me gustan las excepciones, registré OnSaveChanges y tengo esto

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));

¿Qué quiere decir con "Registré los OnSaveChanges"?
Akira Yamamoto

Me conecté a OnSaveChanges en el contexto igual que los demás, simplemente no llegué a la etapa de excepción. \
Mickey Perlstein

2

Este error también ocurre cuando intenta guardar una entidad que tiene errores de validación. Una buena manera de causar esto es olvidarse de comprobar ModelState.IsValid antes de guardar en su base de datos.


2

Asegúrese de que si tiene nvarchar (50) en la fila DB no intente insertar más de 50 caracteres en ella. Estúpido error, pero me llevó 3 horas resolverlo.


1

Esto puede deberse a la cantidad máxima de caracteres permitidos para una columna específica, como en sql un campo puede tener el siguiente tipo de datos, nvarchar(5)pero la cantidad de caracteres ingresados ​​por el usuario es mayor que la especificada, por lo tanto, surge el error.


1

Me enfrenté al mismo problema hace un par de días al actualizar la base de datos. En mi caso, se agregaron algunas nuevas columnas no anulables para mantenimiento que no se proporcionaron en el código que está causando la excepción. Calculo esos campos y los valores proporcionados para ellos y está resuelto.


0

Gracias por sus respuestas, me ayuda mucho. como codifico en Vb.Net, este código Bolt para Vb.Net

Try
   Return MyBase.SaveChanges()
Catch dbEx As Validation.DbEntityValidationException
   For Each [error] In From validationErrors In dbEx.EntityValidationErrors
                       From validationError In validationErrors.ValidationErrors
                       Select New With { .PropertyName = validationError.PropertyName,
                                         .ErrorMessage = validationError.ErrorMessage,
                                         .ClassFullName = validationErrors.Entry.Entity
                                                                    .GetType().FullName}

        Diagnostics.Trace.TraceInformation("Class: {0}, Property: {1}, Error: {2}",
                                           [error].ClassFullName,
                                           [error].PropertyName,
                                           [error].ErrorMessage)
   Next
   Throw
End Try

0

puede ser causado por una propiedad que no está poblada por el modelo ... en cambio, está llena por el controlador ... lo que puede causar este error ... la solución a esto es asignar la propiedad antes de aplicar la validación ModelState. y esta segunda suposición es. Es posible que ya tenga datos en su base de datos e intente actualizarlos, pero ahora los vaya a buscar.


0

En mi caso, tengo una ruta de nombre de columna de tabla cuyo tipo de datos configuré fue varchar (200) .Después de actualizarlo a nvarchar (max), eliminé la tabla de edmx y luego agregué nuevamente la tabla y funcionó correctamente para mí.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.