c# - with - visual studio 2015 entity framework 6




Convalida non riuscita per una o più entità durante il salvataggio delle modifiche al database SQL Server mediante Entity Framework (11)

Voglio salvare il mio Edit to Database e sto usando Entity FrameWork Code-First in ASP.NET MVC 3 / C # ma sto ricevendo errori. Nella mia classe Event, ho datatype DateTime e TimeSpan ma nel mio database, ho rispettivamente Data e ora. Potrebbe essere questa la ragione? Come posso trasmettere al tipo di dati appropriato nel codice prima di salvare le modifiche al database.

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

Metodo nel controller >>>> Problema in 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; } 
}

Database SQL Server 2008 R2 / T-SQL

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

Modulo 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  

Errore del server nell'applicazione '/'

Convalida non riuscita per una o più entità. Vedi la proprietà 'EntityValidationErrors' per maggiori dettagli.

Descrizione: si è verificata un'eccezione non gestita durante l'esecuzione della richiesta Web corrente. Si prega di rivedere la traccia dello stack per ulteriori informazioni sull'errore e sulla sua origine nel codice.

Dettagli eccezione: System.Data.Entity.Validation.DbEntityValidationException: convalida non riuscita per una o più entità. Vedi la proprietà 'EntityValidationErrors' per maggiori dettagli.

Errore di origine:

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

File di origine: C: \ sep \ MvcEventCalendar \ MvcEventCalendar \ Controllers \ EventManagerController.cs Riga: 77

Stack Trace:

[DbEntityValidationException: convalida non riuscita per una o più entità. Vedi la proprietà 'EntityValidationErrors' per maggiori dettagli.]


È possibile estrarre tutte le informazioni da DbEntityValidationException con il seguente codice (è necessario aggiungere gli spazi dei nomi: System.Data.Entity.Validation e System.Diagnostics all'elenco di using ):

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

Assicurati che se hai nvarchar (50) nella riga DB non stai cercando di inserire più di 50 caratteri in esso. Stupido errore ma mi ci sono volute 3 ore per capirlo.


Come miglioramento sia per Praveen che per Tony, uso un override:

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

Ecco un'estensione dell'estensione di Tony ... :-)

Per Entity Framework 4.x, se si desidera ottenere il nome e il valore del campo chiave in modo da sapere quale istanza dell'entità (record DB) ha il problema, è possibile aggiungere quanto segue. Ciò fornisce l'accesso ai membri della classe ObjectContext più potenti dal tuo oggetto 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;

Nel caso in cui tu abbia classi con gli stessi nomi di proprietà, ecco una piccola estensione alla risposta di 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);
       }
    }
 }

Non mi piacciono le eccezioni che ho registrato su OnSaveChanges e ho questo

var validationErrors = model.GetValidationErrors();

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

Penso che l'aggiunta di try / catch per ogni operazione SaveChanges() non sia una buona pratica, è meglio centralizzare questo:

Aggiungi questa classe alla classe principale di 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);
    }
}

Questo sovrascriverà il metodo SaveChanges() tuo contesto e otterrai un elenco separato da virgole contenente tutti gli errori di convalida dell'entità.

anche questo può essere migliorato, per registrare gli errori nell'env di produzione, invece di lanciare un errore.

spero che questo sia utile.


Questa implementazione avvolge l'eccezione di entità in eccezione con il testo di dettaglio. Gestisce DbEntityValidationException , DbUpdateException , errori intervallo datetime2 (MS SQL) e include la chiave dell'entità non valida nel messaggio (utile quando si salvano molte entità in una chiamata SaveChanges ).

Innanzitutto, ignora SaveChanges nella classe 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);
        }
    }

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

Questo errore si verifica anche quando si tenta di salvare un'entità con errori di convalida. Un buon modo per farlo è dimenticare di controllare ModelState.IsValid prima di salvare sul DB.


Thnaks per le tue risposte, mi aiuta molto. come codice in Vb.Net, questo codice Bolt per 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

Non è richiesto alcun cambio di codice:

Mentre sei in modalità di debug all'interno del catch {...} , apri la finestra "QuickWatch" ( Ctrl + Alt + Q ) e incollaci lì:

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

o:

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

Se non si è in un try / catch o non si ha accesso all'oggetto exception.

Ciò ti consentirà di eseguire il drill-down nell'albero ValidationErrors . È il modo più semplice che ho trovato per ottenere una visione immediata di questi errori.







code-first