EF-Code zuerst: Wie wird die Eigenschaft 'EntityValidationErrors' in der Nuget-Paketkonsole angezeigt?

127

Dafür bin ich ratlos:

Ich habe meine Klassen für einen Code-First-Ansatz für Entity Framework (4.1.3) definiert. Alles war in Ordnung (ich habe die Tabellen usw. erstellt), bis ich anfing zu säen.

Jetzt, wenn ich das mache

Add-Migration "remigrate" ; Update-Database;

Auf der Paketkonsole wird die Fehlermeldung "Die Überprüfung für eine oder mehrere Entitäten ist fehlgeschlagen. Weitere Informationen finden Sie in der Eigenschaft 'EntityValidationErrors'."

Ich habe einen Haltepunkt in meiner Seed () -Methode, aber da ich diesen auf der Konsole ausführe, wenn das Projekt nicht ausgeführt wird, weiß ich nicht, wie ich zu den Details komme (PS - Ich habe gesehen, dass die Thread- Validierung fehlgeschlagen ist für eine oder mehrere Entitäten beim Speichern von Änderungen an der SQL Server-Datenbank mithilfe von Entity Framework, das zeigt, wie ich die Eigenschaft sehen kann.)

Ich weiß, dass meine Seed () -Methode ein Problem hat, denn wenn ich direkt nach dem Methodenaufruf eine Rückgabe mache, verschwindet der Fehler. Wie setze ich meinen Haltepunkt, damit ich den Validierungsfehler sehen kann? Ein bisschen verloren. Oder gibt es eine andere Möglichkeit, es in der Nuget-Konsole zu verfolgen?

Jeremy
quelle
Schnelles Update: Ich habe mein Problem gelöst, indem ich jede Variable in meiner Methode systematisch verfolgt habe, bis ich herausgefunden habe, was den Fehler verursacht hat. Ich würde jedoch gerne die Antwort auf meine Frage wissen, da dies viel schneller gehen würde!
Jeremy
Ich denke, Sie könnten die Migration programmgesteuert ausführen und dann die Ausnahme abfangen und die Fehler wiederholen. Es ist nicht ideal, könnte Ihnen aber die Details geben, die Sie benötigen.
Pawel
Frustrierend, wenn die falsche Antwort ganz oben auf den Antworten steht und alle Ehre macht. Ein Ort, an dem StackOverflow eindeutig zu kurz kommt!
Jwize
Wenn Sie Entity Framework verwenden , können Sie sich meine Antwort auf Lösung für „Validierung für eine oder mehrere Entitäten fehlgeschlagen“ ansehen . Weitere Informationen finden Sie unter der Eigenschaft 'EntityValidationErrors' . Hoffe das hilft ...
Murat Yıldız

Antworten:

216

Das hat mich in letzter Zeit auch geärgert. Ich habe das Problem behoben, indem ich in der Seed-Methode eine Wrapper-Funktion in die Configuration-Klasse eingefügt und SaveChangesstattdessen Aufrufe von durch Aufrufe meiner Funktion ersetzt habe. Diese Funktion würde einfach die Fehler in der EntityValidationErrorsSammlung auflisten und eine Ausnahme erneut auslösen, in der die Ausnahmemeldung die einzelnen Probleme auflistet. Dadurch wird die Ausgabe in der NuGet-Paketmanagerkonsole angezeigt.

Code folgt:

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

Ersetzen Sie einfach Aufrufe von context.SaveChanges()durch SaveChanges(context)in Ihrer Seed-Methode.

Richard
quelle
Richard, endlich! Jemand mit einer Idee. Ich werde auf diese Frage zurückkommen, sobald ich sie ausprobiert habe.
Jeremy
Dies hilft wirklich, die Gemeinheiten aufzuspüren :)
Eminem
3
Ich habe diese Technik verwendet, aber stattdessen eine Überschreibung von Sicherungsänderungen im Kontext verwendet. public override int SaveChanges() im Kontext.
Kirsten Greed
5
Es ist effektiver, Teilklassen zu verwenden, wie ich unten geantwortet habe.
Jwize
1
Wenn Sie UserManager-Vorgänge in Ihrer Seed-Methode ausführen, werden bei dieser Änderung die Validierungsfehler nicht in die Ausgabe einbezogen. Sie müssen die Methoden DBContext SaveChanges, SaveChangesAsync und SaveChangesAsync (CT) gemäß der Antwort @jwize überschreiben.
Carl
115

Erweitern Sie Ihre DBContext-Klasse bereits mit einer Teilklassendefinition!

Wenn Sie sich die Klassendefinition für Ihren DbContext ansehen, sieht sie ungefähr so ​​aus:

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

In einer anderen Datei können Sie also dieselbe Definition erstellen und die gewünschten Teile überschreiben.

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

Die ganze Idee mit Teilklassen - haben Sie bemerkt, dass der DbContext eine Teilklasse ist - ist, dass Sie eine generierte Klasse erweitern (oder Klassen in mehreren Dateien organisieren) können. In unserem Fall möchten wir auch die SaveChanges- Methode überschreiben aus einer Teilklasse heraus , die dem DbContext hinzugefügt wird .

Auf diese Weise können wir Fehler-Debugging-Informationen von allen vorhandenen DbContext / SaveChanges-Aufrufen überall erhalten und müssen Ihren Seed-Code oder Entwicklungscode überhaupt nicht ändern.

Dies ist, was ich tun würde ( HINWEIS: Der Unterschied besteht darin, dass ich die SaveChanges-Methode in unserer eigenen DbContext-Teilklasse überschreibe , NICHT in der GENERIERTEN ). Stellen Sie außerdem sicher, dass Ihre Teilklasse den richtigen Namespace verwendet, da Sie sonst Ihren Kopf gegen die Wand schlagen.

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
quelle
Du bist ein Genie ...!
Florian F.
Tolle Lösung. Die Leute sollten alle Antworten lesen, bevor sie abstimmen.
Guilherme de Jesus Santos
3
Sie sollten auch SaveChangesAsync und SaveChangesAsync (CancellationToken) überschreiben - zumindest ist dies bei Code zuerst der Fall, nicht sicher, ob model / db zuerst vorhanden ist.
Carl
@jwize. Ihre Antwort hat mir in meiner Datenbank geholfen, zuerst Probleme bei der Ausnahmebehandlung zu modellieren. tolle Antwort
3355307
1
Bei Verwendung von CodeFirst wird der DbContext offensichtlich nicht generiert. Wenn Sie jedoch den Designer verwenden, werden die Klassen DbContext und Entity generiert und müssen mit einer Teilklasse überschrieben werden.
Jwize
35

Ich habe Richards Antwort in eine Erweiterungsmethode konvertiert:

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

Rufen Sie so an:

context.SaveChangesWithErrors();
Gordo
quelle
4

Ich habe die Version von craigvl in C # konvertiert. Ich musste context.SaveChanges () hinzufügen. damit es für mich wie unten funktioniert.

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
quelle
3

Richard, danke, dass Sie mich auf den richtigen Weg gebracht haben (hatte das gleiche Problem). Dies ist eine Alternative ohne den Wrapper, der bei der Seed-Methode für die Migrationskonfiguration für mich funktioniert hat:

 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

Konnte dann die Ausnahme in der Paketmanager-Konsole sehen. Hoffe das hilft jemandem.

craigvl
quelle
-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
quelle
1
Wäre schön, wenn Sie Ihren Code so formatieren, dass sich Ihr Text nicht in einem Codeblock befindet.
jmattheis
Dies ist wahrscheinlich die schlechteste formatierte Stackoverflow-Antwort, die ich je gesehen habe
crazy_crank