EF Code First: Jak mogę zobaczyć właściwość „EntityValidationErrors” z konsoli pakietu NuGet?

127

Brakuje mi tego:

Zdefiniowałem moje klasy dla frameworka encji (4.1.3) kodu najpierw podejście. Wszystko było w porządku (tworzyłem tabele itp.), Dopóki nie zacząłem Seed.

Teraz, kiedy robię

Add-Migration "remigrate" ; Update-Database;

W konsoli pakietu pojawia się błąd „Walidacja nie powiodła się dla co najmniej jednej jednostki. Aby uzyskać więcej informacji, zobacz właściwość„ EntityValidationErrors ””.

Mam punkt przerwania w mojej metodzie Seed (), ale ponieważ uruchamiam to na konsoli, gdy projekt nie jest uruchomiony, nie mam pojęcia, jak dotrzeć do szczegółów (PS - widziałem wątek Walidacja nie powiodła się dla co najmniej jednej jednostki podczas zapisywania zmian w bazie danych SQL Server przy użyciu Entity Framework, która pokazuje, jak mogę zobaczyć tę właściwość.)

Wiem, że moja metoda Seed () ma problem, ponieważ jeśli wstawię powrót bezpośrednio po wywołaniu metody, błąd zniknie. Jak więc ustawić punkt przerwania, aby zobaczyć, na czym polega błąd walidacji? Trochę przegrane. Czy jest jakiś inny sposób śledzenia go w konsoli nuget?

Jeremy
źródło
Szybka aktualizacja: rozwiązałem swój problem, systematycznie śledząc każdą zmienną w mojej metodzie, aż znalazłem przyczynę błędu. Jednak nadal chciałbym poznać odpowiedź na moje pytanie, ponieważ byłoby to znacznie szybsze!
jeremy
Myślę, że możesz programowo uruchomić migrację, a następnie złapać wyjątek i powtórzyć błędy. Nie jest to idealne rozwiązanie, ale może podać potrzebne szczegóły.
Paweł
Frustrujące, gdy zła odpowiedź znajduje się na początku odpowiedzi i dostaje cały zasługę. Miejsce, w którym StackOverflow wyraźnie nie spełnia wymagań!
jwize
Jeśli używasz Entity Framework , możesz rzucić okiem na moją odpowiedź na temat rozwiązania dla „Walidacja nie powiodła się dla co najmniej jednej jednostki. Aby uzyskać więcej informacji, zobacz właściwość „EntityValidationErrors” . Mam nadzieję, że to pomoże ...
Murat Yıldız

Odpowiedzi:

217

Ostatnio też mnie to zirytowało. Naprawiłem to, umieszczając funkcję opakowującą w klasie Configuration w metodzie Seed i zamiast tego zastąpiłem wywołania SaveChangesfunkcji wywołaniami mojej funkcji. Ta funkcja po prostu wyliczy błędy w EntityValidationErrorskolekcji i ponownie zgłosi wyjątek, w którym komunikat o wyjątku zawiera listę poszczególnych problemów. Dzięki temu dane wyjściowe są wyświetlane w konsoli Menedżera pakietów NuGet.

Kod następujący:

/// <summary>
/// Wrapper for SaveChanges adding the Validation Messages to the generated exception
/// </summary>
/// <param name="context">The context.</param>
private void SaveChanges(DbContext context) {
    try {
        context.SaveChanges();
    } catch (DbEntityValidationException ex) {
        StringBuilder sb = new StringBuilder();

        foreach (var failure in ex.EntityValidationErrors) {
            sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
            foreach (var error in failure.ValidationErrors) {
                sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
                sb.AppendLine();
            }
        }

        throw new DbEntityValidationException(
            "Entity Validation Failed - errors follow:\n" + 
            sb.ToString(), ex
        ); // Add the original exception as the innerException
    }
}

Wystarczy zastąpić wywołań context.SaveChanges()ze SaveChanges(context)w metodzie nasion.

Richard
źródło
Richard, w końcu! Ktoś z pomysłem. Wrócę do tego pytania, gdy spróbuję.
jeremy
To naprawdę pomaga namierzyć okropności :)
Eminem,
3
Użyłem tej techniki, ale zamiast tego użyłem zastąpienia zmian zapisów w kontekście. public override int SaveChanges() wewnątrz kontekstu.
Kirsten Greed,
5
Bardziej efektywne jest użycie klas częściowych, na co odpowiedziałem poniżej.
jwize
1
Jeśli wykonujesz operacje UserManager w metodzie inicjatora, ta zmiana nie będzie obejmować błędów walidacji w danych wyjściowych, musisz zastąpić metody DBContext SaveChanges, SaveChangesAsync i SaveChangesAsync (CT) zgodnie z odpowiedzią @jwize.
Carl
115

Rozszerz swoją klasę DBContext już z częściową definicją klasy!

Jeśli spojrzysz na definicję klasy dla swojego DbContext, będzie to coś podobnego do następującego:

// DatabaseContext.cs   -- This file is auto generated and thus shouldn't be changed. 
public partial class [DatabaseContextName] : DbContext { ... }

Tak więc w innym pliku możesz utworzyć tę samą definicję i nadpisać części, które chcesz.

// partialDatabaseContext.cs  -- you can safely make changes 
// that will not be overwritten in here.
public partial class [DatabaseContextName] : DbContext { // Override defaults here } 

Cały pomysł z częściowym klas --did można zauważyć DbContext jest częściowym class-- jest to, że można rozszerzyć klasę, który został wygenerowany (lub zorganizować zajęcia na kilka plików), aw naszym przypadku chcemy też zastąpić ten SaveChanges metody z klasy częściowej, która dodaje do DbContext .

W ten sposób możemy uzyskać informacje o błędach debugowania ze wszystkich istniejących wywołań DbContext / SaveChanges wszędzie i nie musimy w ogóle zmieniać kodu początkowego ani kodu programistycznego.

Oto, co bym zrobił ( UWAGA różnica polega na tym, że po prostu nadpisuję metodę SaveChanges w naszej własnej autorskiej klasie częściowej DbContext , a NIE w GENEROWANEJ ). Upewnij się również, że część zajęć używa poprawnej przestrzeni nazw, w przeciwnym razie będziesz walił głową o ścianę.

public partial class Database : DbContext
{
    public override int SaveChanges()
    {
        try
        {
            return base.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            var sb = new StringBuilder();

            foreach (var failure in ex.EntityValidationErrors)
            {
                sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
                foreach (var error in failure.ValidationErrors)
                {
                    sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
                    sb.AppendLine();
                }
            }

            throw new DbEntityValidationException(
                "Entity Validation Failed - errors follow:\n" +
                sb.ToString(), ex
                ); // Add the original exception as the innerException
        }
    }
}
jwize
źródło
Jesteś geniuszem ...!
Florian F.,
Świetne rozwiązanie. Przed przystąpieniem do głosowania użytkownicy powinni przeczytać wszystkie odpowiedzi.
Guilherme de Jesus Santos,
3
Powinieneś także zastąpić SaveChangesAsync i SaveChangesAsync (CancellationToken) - przynajmniej tak jest w przypadku kodu jako pierwszego, nie mając pewności co do modelu / db najpierw.
Carl
@jwize. Twoja odpowiedź pomogła mi w mojej bazie danych pierwszych problemów z obsługą wyjątków modelowania. świetna odpowiedź
3355307
1
Podczas korzystania z CodeFirst DbContext oczywiście nie jest generowany. Jednak w przypadku korzystania z projektanta klasy DbContext i Entity są generowane i muszą zostać zastąpione przy użyciu klasy częściowej.
jwize
35

Przekonwertowałem odpowiedź Richardsa na metodę rozszerzenia:

  public static int SaveChangesWithErrors(this DbContext context)
    {
        try
        {
            return context.SaveChanges();
        }
        catch (DbEntityValidationException ex)
        {
            StringBuilder sb = new StringBuilder();

            foreach (var failure in ex.EntityValidationErrors)
            {
                sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
                foreach (var error in failure.ValidationErrors)
                {
                    sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
                    sb.AppendLine();
                }
            }

            throw new DbEntityValidationException(
                "Entity Validation Failed - errors follow:\n" +
                sb.ToString(), ex
            ); // Add the original exception as the innerException
        }
    }

Zadzwoń tak:

context.SaveChangesWithErrors();
Gordo
źródło
4

Przekonwertowałem wersję Craigvla na C # Musiałem dodać context.SaveChanges (); aby działało dla mnie jak poniżej.

try
{
    byte[] bytes = System.IO.File.ReadAllBytes(@"C:\Users\sheph_000\Desktop\Rawr.png");
    Console.WriteLine(bytes);

    context.BeverageTypes.AddOrUpdate(
        x => x.Name,
        new AATPos.DAL.Entities.BeverageType { ID = 1, Name = "Sodas" }
        );

    context.Beverages.AddOrUpdate(
        x => x.Name,
        new AATPos.DAL.Entities.Beverage { ID = 1, Name = "Coke", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" },
        new AATPos.DAL.Entities.Beverage { ID = 2, Name = "Fanta", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" },
        new AATPos.DAL.Entities.Beverage { ID = 3, Name = "Sprite", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" },
        new AATPos.DAL.Entities.Beverage { ID = 4, Name = "Cream Soda", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" },
        new AATPos.DAL.Entities.Beverage { ID = 5, Name = "Pepsi", BeverageTypeID = 1, ImageData = bytes, IsStocked = true, StockLevel = 10, Price = 10.00M, ImageMimeType = "test" }
        );

    context.SaveChanges();
}
catch (System.Data.Entity.Validation.DbEntityValidationException ex)
{
    var sb = new System.Text.StringBuilder();
    foreach (var failure in ex.EntityValidationErrors)
            {
                sb.AppendFormat("{0} failed validation", failure.Entry.Entity.GetType());
        foreach (var error in failure.ValidationErrors)
                {
            sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
            sb.AppendLine();
                }
            }

    throw new Exception(sb.ToString());
}
Ben Pretorius
źródło
3

Richard dzięki za doprowadzenie mnie na właściwą ścieżkę (miał ten sam problem) poniżej jest alternatywą bez opakowania, które zadziałało dla mnie w metodzie inicjującej konfiguracji migracji:

 Protected Overrides Sub Seed(context As NotificationContext)

        Try
            context.System.AddOrUpdate(
               Function(c) c.SystemName,
                New E_NotificationSystem() With {.SystemName = "System1"},
                New E_NotificationSystem() With {.SystemName = "System2"},
                New E_NotificationSystem() With {.SystemName = "System3"})

            context.SaveChanges()

        Catch ex As DbEntityValidationException

            Dim sb As New StringBuilder

            For Each failure In ex.EntityValidationErrors

                sb.AppendFormat("{0} failed validation" & vbLf, failure.Entry.Entity.[GetType]())

                For Each [error] In failure.ValidationErrors
                    sb.AppendFormat("- {0} : {1}", [error].PropertyName, [error].ErrorMessage)
                    sb.AppendLine()
                Next
            Next

            Throw New Exception(sb.ToString())

        End Try
End Sub

Był wtedy w stanie zobaczyć wyjątek w konsoli menedżera pakietów. Mam nadzieję, że to komuś pomoże.

craigvl
źródło
-1

I Also had same model validation problem but successfully catch by myself after lot of thinking;

I use reverse engineering method to catch the problem out of Over 80 + Model Classes;

1> Made copy of dbcontext, changing the name (I add "1" at end and make respective changes in class constructor and initialization etc.

Old:
 
>public class AppDb : IdentityDbContext<ApplicationUser>
>     
> {
> public AppDb(): base("DefaultConnection", throwIfV1Schema: false)
> {
> 
> }
>     
> public static AppDb Create()
>{
>return new AppDb();
>} 

**New:**

>public class AppDb1 : IdentityDbContext<ApplicationUser>
>{
>public AppDb1()
>: base("DefaultConnection", throwIfV1Schema: false)
>{
>}
> 
>public static AppDb1 Create()
> {
> return new AppDb1();
>  }`

...
2> Make changes to Codefirst Migration Configuration from Old DbContext to my new Context.

> internal sealed class Configuration :
> DbMigrationsConfiguration<DAL.AppDb1> { public Configuration() {
> AutomaticMigrationsEnabled = false; }    protected override void
> Seed(DAL.AppDb1 context) {`

3> Comment the Dbsets in new DbContext which was doubt.
4> Apply update migration if succeeded the probelm lye in Commented section.
5> if not then commented section is clear of bug clear.
6> repeat the (4) until found the right place of bug.
7> Happy Codding

Muhammad Usama
źródło
1
Byłoby miło, gdybyś sformatował swój kod tak, aby tekst nie znajdował się wewnątrz bloku kodu.
jmattheis
to jest prawdopodobnie najgorsza sformatowana odpowiedź typu stackoverflow, jaką kiedykolwiek widziałem
crazy_crank