125 votes

EF Code First : Comment voir la propriété 'EntityValidationErrors' dans la console du paquet nuget ?

Je ne sais pas quoi faire :

J'ai défini mes classes pour un cadre d'entité (4.1.3) approche code first. Tout allait bien (je créais les tables etc.) jusqu'à ce que je commence à Seed.

Maintenant, quand je fais le

Add-Migration "remigrate" ; Update-Database;

J'obtiens une erreur sur la console du paquet "Validation failed for one or more entities. Voir la propriété 'EntityValidationErrors' pour plus de détails."

J'ai un point d'arrêt dans ma méthode Seed() mais comme je l'exécute sur la console alors que le projet n'est pas en cours d'exécution, je ne sais pas comment accéder aux détails (PS - j'ai vu le fil de discussion La validation a échoué pour une ou plusieurs entités lors de l'enregistrement des modifications dans la base de données du serveur SQL à l'aide d'Entity Framework. qui montre comment je peux voir la propriété).

Je sais que ma méthode Seed() a un problème car si je mets un retour juste après l'appel de la méthode, l'erreur disparaît. Alors comment puis-je définir mon point d'arrêt pour voir quelle est l'erreur de validation ? Je suis un peu perdu. Ou y a-t-il un autre moyen de la tracer dans la console nuget ?

213voto

Richard Points 8614

J'ai été ennuyé par cela récemment aussi. Je l'ai corrigé en mettant une fonction wrapper dans la classe Configuration dans la méthode Seed, et en remplaçant les appels à SaveChanges avec des appels à ma fonction à la place. Cette fonction va simplement énumérer les erreurs dans le fichier EntityValidationErrors et relance une exception dont le message d'exception énumère les différents problèmes. Ainsi, la sortie s'affiche dans la console du gestionnaire de paquets NuGet.

Le code est le suivant :

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

Il suffit de remplacer les appels à context.SaveChanges() con SaveChanges(context) dans votre méthode de semis.

113voto

jwize Points 823

Étendez votre classe DBContext déjà avec une définition de classe partielle !

Si vous regardez la définition de la classe de votre DbContext, elle ressemblera à ce qui suit :

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

Ainsi, dans un autre fichier, vous pouvez créer la même définition et remplacer les parties que vous souhaitez.

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

Le principe des classes partielles -- avez-vous remarqué le DbContext est une classe partielle-- est que vous pouvez étendre une classe qui a été générée (ou organiser les classes en plusieurs fichiers) et dans notre cas, nous voulons également contourner le site SaveChanges à l'intérieur d'une classe partielle qui s'ajoute à la méthode DbContext .

De cette façon, nous pouvons obtenir des informations de débogage des erreurs à partir de tous les appels DbContext/SaveChanges existants partout et ne pas avoir à modifier du tout votre code Seed ou votre code de développement.

C'est ce que je ferais ( NOTE La différence est que je surcharge la méthode SaveChanges dans la méthode que nous avons créée. DbContext classe partielle, PAS CELUI QUI A ÉTÉ GÉNÉRÉ ). De plus, assurez-vous que votre classe partielle utilise l'option espace de nom correct ou vous vous frapperez la tête contre le mur.

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

35voto

Gordo Points 221

J'ai converti la réponse de Richards en une méthode d'extension :

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

Appelez comme ça :

context.SaveChangesWithErrors();

4voto

Ben Pretorius Points 1046

J'ai converti la version de Craigvl en C#. J'ai dû ajouter context.SaveChanges() ; pour qu'elle fonctionne pour moi comme ci-dessous.

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

3voto

craigvl Points 136

Richard, merci de m'avoir mis sur la bonne voie (j'avais le même problème). Voici une alternative sans le wrapper qui a fonctionné pour moi avec la méthode de la graine de configuration de la migration :

 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

J'ai ensuite pu voir l'exception dans la console du gestionnaire de paquets. J'espère que cela aidera quelqu'un.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X