Die Überprüfung für eine oder mehrere Entitäten ist fehlgeschlagen, während Änderungen an der SQL Server-Datenbank mithilfe von Entity Framework gespeichert wurden

337

Ich möchte meine Bearbeitung in Datenbank speichern und verwende Entity FrameWork Code-First in ASP.NET MVC 3 / C #, erhalte jedoch Fehler. In meiner Ereignisklasse habe ich die Datentypen DateTime und TimeSpan, aber in meiner Datenbank habe ich Datum und Uhrzeit. Könnte dies der Grund sein? Wie kann ich den entsprechenden Datentyp im Code umwandeln, bevor ich Änderungen an der Datenbank speichere?

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

Methode im Controller >>>> Problem bei 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);
    }
}

mit

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

SQL Server 2008 R2-Datenbank / T-SQL

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

HTTP-Formular

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  

Serverfehler in '/' Anwendung.

Die Validierung für eine oder mehrere Entitäten ist fehlgeschlagen. Weitere Informationen finden Sie unter der Eigenschaft 'EntityValidationErrors'.

Beschreibung: Während der Ausführung der aktuellen Webanforderung ist eine nicht behandelte Ausnahme aufgetreten. Bitte überprüfen Sie die Stapelverfolgung, um weitere Informationen über den Fehler und dessen Ursprung im Code zu erhalten.

Ausnahmedetails: System.Data.Entity.Validation.DbEntityValidationException: Die Überprüfung für eine oder mehrere Entitäten ist fehlgeschlagen. Weitere Informationen finden Sie unter der Eigenschaft 'EntityValidationErrors'.

Quellfehler:

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

Quelldatei: C: \ sep \ MvcEventCalendar \ MvcEventCalendar \ Controllers \ EventManagerController.cs Zeile: 77

Stapelverfolgung:

[DbEntityValidationException: Die Überprüfung für eine oder mehrere Entitäten ist fehlgeschlagen. Weitere Informationen finden Sie in der Eigenschaft 'EntityValidationErrors'.]

user522767
quelle
8
Wahrscheinlich hat eines Ihrer erforderlichen Felder einen Nullwert. Wie EventDate, StartTime, Preis, Kategorie usw.
Daveo
Haben Sie die gesendeten Formularvariablen überprüft, um sicherzustellen, dass jede mit dem datenbankdefinierten Typ übereinstimmt? Oder wie Daveo sagte, fehlt einer der erforderlichen Formularwerte ...
Timothy Clifford
Nicht alle veröffentlichten Formularvariablen stimmen mit dem datenbankdefinierten Typ überein. Ich habe Datum und Uhrzeit in der Datenbank verwendet, aber in .NET gibt es kein direktes Datentypäquivalent. Daher habe ich DateTime und TimeSpan verwendet. Jetzt muss ich die beiden auf Datum und Uhrzeit umrechnen.
user522767
Die Fehlerquelle für mich war ein Zeichenfolgenfeld mit mehr als 30 Zeichen und meiner Feldgröße in der Datenbank war 30.
Mojtaba

Antworten:

840

Sie können alle Informationen aus dem DbEntityValidationExceptionmit dem folgenden Code extrahieren (Sie müssen die Namespaces hinzufügen: System.Data.Entity.Validationund System.Diagnosticszu Ihrer usingListe):

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);
        }
    }
}
Praveen Prasad
quelle
7
Sie können diesen Fehler auch erhalten, wenn Seed-Daten die Modellattributregeln (wie Required) nicht vollständig erfüllen . Ich habe eine Antwort mit etwas mehr Informationen hinzugefügt.
Dan
8
Die Antwort könnte verbessert werden, indem erklärt wird, wo Sie die Trace-Ausgabe tatsächlich sehen können.
mkataja
1
Ich fand diesen MSDN-Artikel über Trace sehr nützlich
Borik
2
Sie, Sir, haben das Internet gewonnen.
Leandro
8
Die Trace-Ausgabe wird im Ausgabefenster angezeigt. Klicken Sie oben auf
Debuggen
249

Keine Codeänderung erforderlich:

catch {...}Öffnen Sie im Debug-Modus innerhalb des Blocks das Fenster "QuickWatch" ( Ctrl+ Alt+ Q) und fügen Sie es dort ein:

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

oder:

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

Wenn Sie sich nicht in einem Versuch / Fang befinden oder keinen Zugriff auf das Ausnahmeobjekt haben.

Auf diese Weise können Sie einen Drilldown in den ValidationErrorsBaum durchführen. Dies ist der einfachste Weg, um sofort einen Einblick in diese Fehler zu erhalten.

GONeale
quelle
6
Du, mein Freund, bist ein Genie, dass du mir geholfen hast, den ET-Fehler aufzuspüren, den ich erhalten habe. Danke!
Mark Kram
4
Kein Problem :) Aber kein Genie, liebe QuickWatch :)
GONeale
4
Die Antwort wurde nur für diejenigen aktualisiert, die keinen Zugriff auf das Ausnahmeobjekt / die Ausnahmevariable haben.
GONeale
7
Jedes Mal, wenn ich diese Ausnahme erhalte, suche ich nach dem Fehler, dann komme ich hierher (2. Ergebnis bei Google), finde diesen Beitrag und verwende die zweite Lösung im Bereich Debug> Beobachten. Dutzende Male. Danke GONeale.
Ata S.
1
Bin ich der einzige, der beim Ausführen der zweiten Abfrage die Meldung "Der Name '$ exception' existiert im aktuellen Kontext nicht" erhält?
Alex Klaus
37

Für den Fall, dass Sie Klassen mit denselben Eigenschaftsnamen haben, finden Sie hier eine kleine Erweiterung der Antwort von 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);
       }
    }
 }
Tony
quelle
22

Als Verbesserung für Praveen und Tony verwende ich einen 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...
        }
    }
}
Blitz Donner
quelle
6

Diese Implementierung umschließt die Entität von Ausnahme zu Ausnahme mit Detailtext. Es behandelt DbEntityValidationException, DbUpdateException, datetime2Bereichsfehler (MS SQL), und umfassen zahlreiche ungültige Entität in Meldung (nützlich , wenn savind viele Einheiten an einem SaveChangesAnruf).

Überschreiben Sie zunächst die SaveChangesDbContext-Klasse:

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

ExceptionHelper-Klasse:

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);
    }
}
Sel
quelle
Sie sollten auf den SaveChangesAsync-Aufruf warten, wenn Sie die Ausnahme behandeln möchten.
Arnab Chakraborty
Danke, Arnab Chakraborty ! Antwort behoben
Sel
5

Dieser Code hat mir geholfen, mein Problem zu finden, als ich ein Problem mit meinem Entity VAlidation Erros hatte. Es zeigte mir das genaue Problem mit meiner Entitätsdefinition. Versuchen Sie den folgenden Code, in dem Sie storeDB.SaveChanges () abdecken müssen. im folgenden versuchen Sie Catch Block.

  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;
}
Рахул Маквана
quelle
4

Ich habe diesen Fehler heute erhalten und konnte ihn eine Weile nicht beheben, aber ich habe festgestellt, RequireAttributedass einige Modelle zu meinen Modellen hinzugefügt wurden und dass einige Entwicklungs-Seed-Daten nicht alle erforderlichen Felder ausfüllten.
Nur eine Anmerkung: Wenn Sie diesen Fehler beim Aktualisieren der Datenbank durch eine Init-Strategie wie diese erhalten DropCreateDatabaseIfModelChanges, müssen Sie sicherstellen, dass Ihre Seed-Daten alle Modelldaten-Validierungsattribute erfüllen und erfüllen .

Ich weiß, dass dies etwas anders ist als das Problem in der Frage, aber es ist eine beliebte Frage, daher dachte ich, ich würde der Antwort für andere, die das gleiche Problem wie ich haben, etwas mehr hinzufügen.
Hoffe das hilft anderen :)

dan richardson
quelle
4

Ich denke, das Hinzufügen von try / catch für jede SaveChanges()Operation ist keine gute Praxis. Es ist besser, dies zu zentralisieren:

Fügen Sie diese Klasse der Hauptklasse hinzu 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);
    }
}

Dadurch wird die SaveChanges()Methode Ihres Kontexts überschrieben und Sie erhalten eine durch Kommas getrennte Liste mit allen Fehlern bei der Entitätsüberprüfung.

Dies kann auch verbessert werden, um Fehler in der Produktionsumgebung zu protokollieren, anstatt nur einen Fehler auszulösen.

hoffe das ist hilfreich.

Chtiwi Malek
quelle
Wo finde ich die "DbContext-Klasse"? Ich bin ein bisschen neu in all diesen MVC-Sachen. Ich verwende MVC 5 und Entity Framework 6. Ich weiß nur nicht, wie der Name in meinem Projektmappen-Explorer aussehen soll.
JustJohn
@JustJohn Es befindet sich in Ihrem Datenbankmodell (Klassendatei). Wenn Sie nicht wissen, wo es sich befindet, können Sie das Schlüsselwort vollständig nach DbContextIhrem Projekt durchsuchen .
Chtiwi Malek
1
Ich mag diesen Ansatz am besten, da er eine universelle Lösung in der App ist und leicht enthüllt, was sich unter der Oberfläche befindet, ohne viel für andere Module im System zu
ändern
3

Hier ist eine Erweiterung zu Tonys Erweiterung ... :-)

Wenn Sie für Entity Framework 4.x den Namen und den Wert des Schlüsselfelds abrufen möchten, damit Sie wissen, bei welcher Entitätsinstanz (DB-Datensatz) das Problem auftritt, können Sie Folgendes hinzufügen. Dies bietet Zugriff auf die leistungsstärkeren ObjectContext-Klassenmitglieder von Ihrem DbContext-Objekt aus.

// 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;
Martin_W
quelle
3

Ich mag keine Ausnahmen. Ich habe die OnSaveChanges registriert und habe diese

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));
Mickey Perlstein
quelle
Was meinst du mit "Ich habe die OnSaveChanges registriert"?
Akira Yamamoto
Ich habe mich wie die anderen mit den OnSaveChanges im Kontext verbunden, ich habe nur die Ausnahmestufe nicht erreicht. \
Mickey Perlstein
2

Dieser Fehler tritt auch auf, wenn Sie versuchen, eine Entität mit Validierungsfehlern zu speichern. Eine gute Möglichkeit, dies zu verursachen, besteht darin, zu vergessen, ModelState.IsValid zu überprüfen, bevor Sie es in Ihrer Datenbank speichern.

Laylarenee
quelle
2

Stellen Sie sicher, dass Sie nicht versuchen, mehr als 50 Zeichen einzufügen, wenn Sie nvarchar (50) in der DB-Zeile haben. Dummer Fehler, aber ich habe 3 Stunden gebraucht, um es herauszufinden.

user4030144
quelle
1

Dies kann an der maximalen Anzahl von Zeichen liegen, die für eine bestimmte Spalte zulässig sind, wie in SQL. Ein Feld hat möglicherweise den folgenden Datentyp, nvarchar(5)aber die Anzahl der vom Benutzer eingegebenen Zeichen ist größer als die angegebene. Daher tritt der Fehler auf.

lokopi
quelle
1

Ich habe vor ein paar Tagen das gleiche Problem beim Aktualisieren der Datenbank festgestellt. In meinem Fall wurden nur wenige neue nicht nullbare Spalten für die Wartung hinzugefügt, die nicht im Code enthalten waren, der die Ausnahme verursacht. Ich finde diese Felder heraus und lieferte Werte für sie und ihre Auflösung.

Jhabar
quelle
0

Danke für deine Antworten, es hilft mir sehr. Wie ich in Vb.Net codiere, ist dieser Bolzencode für 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
Exatex
quelle
0

Dies kann durch eine Eigenschaft verursacht werden, die nicht vom Modell ausgefüllt wird. Stattdessen wird sie vom Controller ausgefüllt. Dies kann diesen Fehler verursachen. Die Lösung hierfür besteht darin, die Eigenschaft zuzuweisen, bevor die ModelState-Validierung angewendet wird. und diese zweite Annahme ist. Möglicherweise haben Sie bereits Daten in Ihrer Datenbank und versuchen, diese zu aktualisieren, rufen sie jedoch jetzt ab.

Muhammad Mustafa
quelle
0

In meinem Fall habe ich einen Tabellenspaltennamen Pfad, dessen Datentyp varchar (200) war. Nachdem ich ihn auf nvarchar (max) aktualisiert habe, habe ich die Tabelle aus edmx gelöscht und dann erneut die Tabelle hinzugefügt, und sie wurde für mich ordnungsgemäß ausgeführt.

Saket Singh
quelle