Prima il codice EF: come posso vedere la proprietà 'EntityValidationErrors' dalla console del pacchetto nuget?


127

Sono in perdita per questo:

Ho definito le mie classi per un primo approccio al codice di framework di entità (4.1.3). Tutto andava bene (stavo creando i tavoli ecc.) Fino a quando non ho iniziato a Seed.

Ora quando faccio il

Add-Migration "remigrate" ; Update-Database;

Viene visualizzato un errore sulla console del pacchetto "Convalida non riuscita per una o più entità. Vedere la proprietà 'EntityValidationErrors' per ulteriori dettagli."

Ho un punto di interruzione nel mio metodo Seed () ma poiché sto eseguendo questo sulla console quando il progetto non è in esecuzione, non ho idea di come arrivare ai dettagli (PS - Ho visto il thread Convalida non riuscita per una o più entità durante il salvataggio delle modifiche al database di SQL Server utilizzando Entity Framework che mostra come posso vedere la proprietà.)

So che il mio metodo Seed () ha un problema perché se inserisco un ritorno subito dopo la chiamata del metodo, l'errore scompare. Quindi, come posso impostare il mio punto di interruzione in modo da poter vedere qual è l'errore di convalida? Un po 'perso. O c'è un altro modo per rintracciarlo nella console di Nuget ??


Aggiornamento rapido: ho risolto il mio problema monitorando sistematicamente ogni variabile all'interno del mio metodo fino a quando non ho trovato la causa dell'errore. Tuttavia, mi piacerebbe comunque conoscere la risposta alla mia domanda poiché sarebbe molto più veloce!
Jeremy,

Penso che potresti eseguire la migrazione a livello di codice e quindi catturare l'eccezione e ripetere gli errori. Non è l'ideale ma potrebbe darti i dettagli di cui hai bisogno.
Pawel,

Frustrante quando la risposta sbagliata è in cima alle risposte e ottiene tutto il merito. Un posto in cui StackOverflow non è all'altezza!
jwize,

Se usi Entity Framework puoi dare un'occhiata alla mia risposta su Soluzione per "Convalida non riuscita per una o più entità. Vedi la proprietà 'EntityValidationErrors' per maggiori dettagli . Spero che questo aiuti ...
Murat Yıldız il

Risposte:


216

Anche questo mi ha infastidito di recente. L'ho risolto inserendo una funzione wrapper nella classe Configuration nel metodo Seed e sostituivo invece le chiamate SaveChangescon le chiamate alla mia funzione. Questa funzione enumera semplicemente gli errori all'interno della EntityValidationErrorsraccolta e rinnova un'eccezione in cui il messaggio Eccezione elenca i singoli problemi. Questo fa apparire l'output nella console di gestione dei pacchetti NuGet.

Il codice segue:

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

Sostituisci le chiamate con context.SaveChanges()con SaveChanges(context)nel tuo metodo seed.


Richard, finalmente! Qualcuno con un'idea. Tornerò a questa domanda una volta provata.
Jeremy,

Questo aiuta davvero a rintracciare i coglioni :)
Eminem

3
Ho usato questa tecnica ma ho usato una sostituzione dei savechanges all'interno del contesto. public override int SaveChanges() all'interno del contesto.
Kirsten Greed,

5
È più efficace usare le classi parziali come ho risposto di seguito.
jwize,

1
Se si stanno eseguendo operazioni UserManager nel metodo seed, questa modifica non includerà gli errori di convalida nell'output, è necessario sovrascrivere i metodi DBContext SaveChanges, SaveChangesAsync e SaveChangesAsync (CT) secondo la risposta @jwize.
Carl,

115

Estendi la tua classe DBContext già con una definizione di classe parziale!

Se guardi la definizione della classe per il tuo DbContext, sarà simile alla seguente:

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

Quindi, in un altro file puoi creare la stessa definizione e sovrascrivere le parti che desideri.

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

L'intera idea con classi parziali - hai notato che DbContext è una classe parziale - è che puoi estendere una classe che è stata generata (o organizzare le classi in più file) e nel nostro caso vogliamo anche sovrascrivere il metodo SaveChanges all'interno di una classe parziale che si aggiunge a DbContext .

In questo modo possiamo ottenere informazioni di debug degli errori da tutte le chiamate DbContext / SaveChanges esistenti ovunque e non è necessario modificare il codice seme o il codice di sviluppo.

Questo è quello che farei ( NOTA la differenza è che ho appena ignorato il metodo SaveChanges nella nostra classe parziale DbContext creata , NON LA GENERATA ). Inoltre, assicurati che la tua classe parziale usi lo spazio dei nomi corretto o sbatterai la testa contro il muro.

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

Sei un genio ...!
Florian F.

Ottima soluzione Le persone dovrebbero leggere tutte le risposte prima di votare.
Guilherme de Jesus Santos,

3
Dovresti anche sovrascrivere SaveChangesAsync e SaveChangesAsync (CancelToken) - almeno questo è il caso del codice prima, non sei sicuro del modello / db prima.
Carl,

@jwize. la tua risposta mi ha aiutato nel mio database a modellare i problemi di gestione delle eccezioni. ottima risposta
3355307,

1
Quando si utilizza CodeFirst, DbContext non viene ovviamente generato. Tuttavia, quando si utilizza il designer, vengono generate le classi DbContext ed Entity che devono essere sovrascritte utilizzando una classe parziale.
jwize,

35

Ho convertito la risposta di Richard in un metodo di estensione:

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

Chiama in questo modo:

context.SaveChangesWithErrors();

4

Ho convertito la versione di Craigvl in C # e ho dovuto aggiungere context.SaveChanges (); affinché funzioni per me come di seguito.

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

3

Richard grazie per avermi portato sulla strada giusta (aveva lo stesso problema) di seguito è un'alternativa senza il wrapper che ha funzionato per me nel metodo seed della configurazione della migrazione:

 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

È stato quindi in grado di vedere l'eccezione nella console del gestore pacchetti. Spero che questo aiuti qualcuno.


-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


1
Sarebbe bello quando si formatta il codice che il testo non è all'interno di un blocco di codice.
jmattheis,

questa è probabilmente la risposta di
stackoverflow
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.