Sprawdzanie poprawności nie powiodło się dla jednego lub więcej podmiotów podczas zapisywania zmian w bazie danych SQL Server przy użyciu Entity Framework

337

Chcę zapisać moją edycję w bazie danych i używam Entity FrameWork Code-First w ASP.NET MVC 3 / C #, ale dostaję błędy. W mojej klasie Event mam typy danych DateTime i TimeSpan, ale w mojej bazie danych mam odpowiednio datę i godzinę. Czy to może być powód? Jak mogę rzutować na odpowiedni typ danych w kodzie przed zapisaniem zmian w bazie danych.

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

Metoda w kontrolerze >>>> Problem w 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);
    }
}

z

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

Baza danych SQL Server 2008 R2 / T-SQL

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

Formularz 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  

Błąd serwera w aplikacji „/”.

Sprawdzanie poprawności nie powiodło się dla jednego lub więcej podmiotów. Aby uzyskać więcej informacji, zobacz właściwość „EntityValidationErrors”.

Opis: Wystąpił nieobsługiwany wyjątek podczas wykonywania bieżącego żądania internetowego. Przejrzyj dane śledzenia stosu, aby uzyskać więcej informacji o błędzie i jego źródle w kodzie.

Szczegóły wyjątku: System.Data.Entity.Validation.DbEntityValidationException: Walidacja nie powiodła się dla jednego lub więcej podmiotów. Aby uzyskać więcej informacji, zobacz właściwość „EntityValidationErrors”.

Błąd źródła:

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

Plik źródłowy: C: \ sep \ MvcEventCalendar \ MvcEventCalendar \ Controllers \ EventManagerController.cs Linia: 77

Ślad stosu:

[DbEntityValidationException: Sprawdzanie poprawności nie powiodło się dla jednego lub więcej podmiotów. Więcej informacji można znaleźć we właściwości „EntityValidationErrors”.]

użytkownik522767
źródło
8
prawdopodobnie jedno z wymaganych pól ma wartość zerową. Podobnie jak EventDate, StartTime, Cena, Kategoria itp.
Daveo
Czy sprawdziłeś, czy zmienne formularzy są wysyłane, aby upewnić się, że każdy pasuje do typu zdefiniowanego w bazie danych? Lub, jak powiedział Daveo, brakuje jednej z wymaganych wartości formularza ...
timothyclifford
Nie wszystkie wysłane zmienne formularza są zgodne z typem zdefiniowanym w bazie danych. Użyłem daty i godziny w bazie danych, ale nie ma bezpośredniego odpowiednika typu danych w .NET. Dlatego użyłem DateTime i TimeSpan. Teraz muszę przekonwertować dwa odpowiednio na datę i godzinę.
user522767
Źródłem błędu było dla mnie pole łańcuchowe zawierające ponad 30 znaków, a rozmiar mojego pola w bazie danych wynosił 30.
Mojtaba

Odpowiedzi:

840

Możesz wyodrębnić wszystkie informacje z DbEntityValidationExceptionnastępującego kodu (musisz dodać przestrzenie nazw: System.Data.Entity.Validationi System.Diagnosticsdo swojej usinglisty):

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
źródło
7
Ten błąd można również uzyskać, jeśli jakiekolwiek dane początkowe nie są w pełni zgodne z regułami atrybutów modelu (np Required.). Dodałem odpowiedź z nieco więcej informacji.
dan richardson
8
Odpowiedź można poprawić, wyjaśniając, gdzie faktycznie można zobaczyć dane wyjściowe śledzenia.
mkataja
1
Znalazłem ten artykuł msdn o śledzeniu, który jest bardzo przydatny
Borik
2
Pan wygrał Internet.
Leandro,
8
Wyniki śledzenia można zobaczyć w oknie Dane wyjściowe. Kliknij opcję Debuguj u góry -> Windows -> Dane wyjściowe
reggaeguitar
249

Nie wymaga zmiany kodu:

W trybie debugowania w catch {...}bloku otwórz okno „QuickWatch” ( Ctrl+ Alt+ Q) i wklej tam:

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

lub:

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

Jeśli nie próbujesz / złapiesz lub nie masz dostępu do obiektu wyjątku.

Pozwoli ci to zejść do ValidationErrorsdrzewa. To najłatwiejszy sposób na uzyskanie natychmiastowego wglądu w te błędy.

GONeale
źródło
6
Ty, mój przyjacielu, jesteś geniuszem, pomogłeś mi wyśledzić błąd ET, który otrzymywałem, dziękuję!
Mark Kram
4
Nie ma problemu :) Ale nie genialne, po prostu uwielbiam QuickWatch :)
GONeale
4
Właśnie zaktualizowałem odpowiedź dla tych, którzy nie mają dostępu do obiektu / zmiennej wyjątku.
GONeale 24.01.2013
7
Za każdym razem, gdy otrzymuję ten wyjątek, szukam błędu, a następnie przychodzę tutaj (2. wynik w Google), znajduję ten post i używam drugiego rozwiązania w panelu Debugowanie> Oglądaj. Zrobiłem dziesiątki razy. Dziękuję GONeale.
Ata S.
1
Czy jestem jedynym, który dostaje „Nazwa„ wyjątek $ ”nie istnieje w bieżącym kontekście” po uruchomieniu drugiego zapytania?
Alex Klaus,
37

W przypadku klas o takich samych nazwach własności, oto małe rozszerzenie odpowiedzi 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
źródło
22

Jako ulepszenie zarówno dla Praveen, jak i Tony'ego, używam przesłonięcia:

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...
        }
    }
}
Bolt Thunder
źródło
6

Ta implementacja zawija wyjątek encji do wyjątku z tekstem szczegółowym. Obsługuje DbEntityValidationException, DbUpdateException, datetime2błędy range (MS SQL), a to klucz nieprawidłowych podmiotu w wiadomości (przydatne gdy savind wiele podmiotów w jednej SaveChangesrozmowy).

Po pierwsze, przesłonięcie SaveChangesw klasie 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);
        }
    }

Klasa 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);
    }
}
Sel
źródło
Jeśli chcesz obsłużyć wyjątek, powinieneś poczekać na wywołanie SaveChangesAsync.
Arnab Chakraborty
Dzięki, Arnab Chakraborty ! Odpowiedź naprawiona
Sel
5

Ten kod pomógł znaleźć mój problem, gdy miałem problem z moim błędem VAlidation jednostki. Podał mi dokładny problem z moją definicją jednostki. Wypróbuj następujący kod, w którym musisz opisać storeDB.SaveChanges (); w dalszej części spróbuj złapać blok.

  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;
}
Рахул Маквана
źródło
4

Otrzymałem ten błąd dzisiaj i przez jakiś czas nie mogłem go rozwiązać, ale zdałem sobie sprawę, że to po dodaniu niektórych RequireAttributes do moich modeli i że niektóre dane źródłowe programowania nie zapełniają wszystkich wymaganych pól.
Warto więc pamiętać, że jeśli pojawia się ten błąd podczas aktualizowania bazy danych za pomocą strategii inicjującej, takiej jak DropCreateDatabaseIfModelChangeswtedy, musisz upewnić się, że dane początkowe spełniają i spełniają wszelkie atrybuty sprawdzania poprawności danych modelu .

Wiem, że to nieco różni się od problemu w pytaniu, ale jest to popularne pytanie, więc pomyślałem, że dodam trochę więcej do odpowiedzi dla innych, którzy mają ten sam problem jak ja.
Mam nadzieję, że to pomaga innym :)

Dan Richardson
źródło
4

Myślę, że dodanie try / catch dla każdej SaveChanges()operacji nie jest dobrą praktyką, lepiej jest scentralizować to:

Dodaj tę klasę do DbContextklasy głównej :

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

Spowoduje to zastąpienie SaveChanges()metody kontekstu, a otrzymasz listę oddzieloną przecinkami zawierającą wszystkie błędy sprawdzania poprawności encji.

można to również poprawić, rejestrując błędy w środowisku env, zamiast po prostu zgłaszać błąd.

mam nadzieję, że to jest pomocne.

Chtiwi Malek
źródło
Gdzie mogę znaleźć „klasę DbContext”? Jestem trochę nowy w tych wszystkich sprawach z MVC. Używam MVC 5 i Entity Framework 6. Po prostu nie wiem, jak nazwa powinna wyglądać w Eksploratorze rozwiązań.
JustJohn,
@JustJohn znajduje się w modelu bazy danych (plik klasy), jeśli nie wiesz, gdzie to jest, możesz przeprowadzić pełne wyszukiwanie słowa kluczowego DbContextwzględem swojego projektu.
Chtiwi Malek
1
Najbardziej podoba mi się to podejście, ponieważ jest to uniwersalne rozwiązanie w aplikacji i łatwo ujawnia, co znajduje się pod powierzchnią, nie zmieniając zbytnio innych modułów w systemie
Korayem
3

Oto rozszerzenie rozszerzenia Tony'ego ... :-)

W przypadku Entity Framework 4.x, jeśli chcesz uzyskać nazwę i wartość pola klucza, aby wiedzieć, która instancja encji (rekord DB) ma problem, możesz dodać następujące elementy. Zapewnia to dostęp do potężniejszych członków klasy ObjectContext z Twojego obiektu 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;
Martin_W
źródło
3

Nie lubię wyjątków. Zarejestrowałem OnSaveChanges i mam to

var validationErrors = model.GetValidationErrors();

var h = validationErrors.SelectMany(x => x.ValidationErrors
                                          .Select(f => "Entity: " 
                                                      +(x.Entry.Entity) 
                                                      + " : " + f.PropertyName 
                                                      + "->" + f.ErrorMessage));
Mickey Perlstein
źródło
Co masz na myśli mówiąc „Zarejestrowałem zmiany OnSaveChanges”?
Akira Yamamoto
Podłączyłem się do OnSaveChanges w kontekście tak samo jak inne, po prostu nie osiągnąłem etapu wyjątku. \
Mickey Perlstein
2

Ten błąd występuje również podczas próby zapisania encji, w której występują błędy sprawdzania poprawności. Dobrym sposobem na spowodowanie tego jest zapomnienie o sprawdzeniu ModelState.IsValid przed zapisaniem do DB.

laylarenee
źródło
2

Upewnij się, że jeśli masz nvarchar (50) w wierszu DB, nie próbujesz wstawiać do niego więcej niż 50 znaków. Głupia pomyłka, ale zajęło mi to 3 godziny.

użytkownik4030144
źródło
1

Może to wynikać z maksymalnej liczby znaków dozwolonych dla określonej kolumny, tak jak w sql jedno pole może mieć następujący typ danych, nvarchar(5)ale liczba znaków wprowadzonych przez użytkownika jest większa niż określona, ​​dlatego pojawia się błąd.

lokopi
źródło
1

Kilka dni temu napotkałem ten sam problem podczas aktualizacji bazy danych. W moim przypadku dodano kilka nowych niepustych kolumn do konserwacji, które nie zostały dostarczone w kodzie, który powoduje wyjątek. Zrozumiałem te pola i podałem dla nich wartości i ich rozwiązanie.

Jhabar
źródło
0

Dziękujemy za odpowiedzi, bardzo mi to pomaga. jak i kod w Vb.Net, ten kod Bolt dla 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
źródło
0

może to być spowodowane właściwością, która nie jest wypełniana przez model .. zamiast tego jest wypełniana przez kontroler .. co może powodować ten błąd .. rozwiązaniem tego jest przypisanie właściwości przed zastosowaniem sprawdzania poprawności ModelState. a to drugie założenie jest. być może masz już dane w bazie danych i próbujesz je zaktualizować, ale teraz je pobierasz.

Muhammad Mustafa
źródło
0

W moim przypadku mam nazwę kolumny tabeli Ścieżka, której ustawionym typem danych był varchar (200). Po zaktualizowaniu go do nvarchar (max) usunąłem tabelę z edmx, a następnie ponownie dodałem tabelę i poprawnie dla mnie napisała.

Saket Singh
źródło