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



Answers

No se requiere cambio de código:

Mientras se encuentra en modo de depuración dentro del bloque catch {...} , 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 try / catch o no tiene acceso al objeto de excepción.

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.

Question

Quiero guardar mi edición en la base de datos y estoy usando Entity FrameWork Code-First en ASP.NET MVC 3 / C #, pero recibo errores. En mi clase Event, tengo 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 convertir 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)  

Forma Http

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. Consulte 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 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.

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

Stack Trace:

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




Esta implementación envuelve la excepción de la entidad a la excepción con texto de detalle. Maneja DbEntityValidationException , DbUpdateException , datetime2 range errors (MS SQL) e incluye la clave de la entidad no válida en el mensaje (útil cuando se conocen muchas entidades en una llamada a SaveChanges ).

Primero, anule SaveChanges en 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);
    }
}



Asegúrese de que si tiene nvarchar (50) en la fila DB, no intente insertar más de 50 caracteres en él. Estúpido error, pero tardé 3 horas en descubrirlo.




Como 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...
        }
    }
}



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

Agregue esta clase a la clase principal DbContext :

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 método SaveChanges() su contexto y obtendrá una lista separada por comas que contiene todos los errores de validación de la entidad.

esto también puede mejorarse, registrar errores en el entorno de producción, en lugar de arrojar un error.

Espero que esto sea útil.




puede estar causado por Propiedad que no está poblada por modelo ... en su lugar, está poblada por el Controlador ... que puede causar este error ... solución a esto es asignar la propiedad antes de aplicar la validación de ModelState. y esta segunda Asunción es es posible que ya tenga datos en su base de datos e intente actualizarlos pero ahora los busque.




Me enfrenté al mismo problema hace un par de días mientras actualizaba la base de datos. En mi caso, se agregaron pocas columnas nuevas para el mantenimiento que no se incluyeron en el código que causa la excepción. Calculo esos campos y les proporciono los valores y los resuelve.




No me gustan las excepciones. He registrado OnSaveChanges y tengo este

var validationErrors = model.GetValidationErrors();

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





Links