EF Code First: Comment voir la propriété 'EntityValidationErrors' à partir de la console du package nuget?

127

Je suis perdu pour ça:

J'ai défini mes classes pour une approche de code du framework d'entité (4.1.3). Tout allait bien (je créais les tables, etc.) jusqu'à ce que je commence à semer.

Maintenant, quand je fais le

Add-Migration "remigrate" ; Update-Database;

J'obtiens une erreur sur la console du package "La validation a échoué pour une ou plusieurs entités. Voir la propriété 'EntityValidationErrors' pour plus de détails."

J'ai un point d'arrêt dans ma méthode Seed () mais parce que j'exécute ceci sur la console lorsque 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 thread La validation a échoué pour une ou plusieurs entités lors de l'enregistrement des modifications dans la base de données SQL Server à 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 méthode, l'erreur disparaît. Alors, comment définir mon point d'arrêt pour voir quelle est l'erreur de validation? Un peu perdu. Ou y a-t-il un autre moyen de le retracer dans la console nuget?

Jeremy
la source
Mise à jour rapide: j'ai résolu mon problème en suivant systématiquement chaque variable de ma méthode jusqu'à ce que je trouve la cause de l'erreur. Cependant, j'aimerais toujours connaître la réponse à ma question car ce serait beaucoup plus rapide!
jeremy
Je pense que vous pouvez exécuter la migration par programme, puis attraper l'exception et répéter les erreurs. Ce n'est pas idéal mais pourrait vous donner les détails dont vous avez besoin.
Pawel
Frustrant quand la mauvaise réponse est en haut des réponses et obtient tout le crédit. Un endroit où StackOverflow échoue clairement!
jwize le
Si vous utilisez Entity Framework, vous pouvez consulter ma réponse sur Solution pour «La validation a échoué pour une ou plusieurs entités. Voir la propriété 'EntityValidationErrors' pour plus de détails . J'espère que cela aidera ...
Murat Yıldız

Réponses:

216

J'ai aussi été ennuyé par cela récemment. Je l'ai corrigé en mettant une fonction wrapper dans la classe Configuration de la méthode Seed et en remplaçant les appels à SaveChangespar des appels à ma fonction à la place. Cette fonction énumérerait simplement les erreurs dans la EntityValidationErrorscollection et relancerait une exception où le message Exception répertorie les problèmes individuels. Cela fait apparaître la sortie dans la console du gestionnaire de packages NuGet.

Le code suit:

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

Remplacez simplement les appels à context.SaveChanges()par SaveChanges(context)dans votre méthode de départ.

Richard
la source
Richard, enfin! Quelqu'un avec une idée. Je reviendrai sur cette question une fois que je l’aurai essayé.
jeremy
Cela aide vraiment à traquer les méchants :)
Eminem
3
J'ai utilisé cette technique, mais j'ai utilisé un remplacement des changements de sauvegarde dans le contexte. public override int SaveChanges() dans le contexte.
Kirsten Greed
5
Il est plus efficace d'utiliser des classes partielles comme je l'ai répondu ci-dessous.
jwize
1
Si vous effectuez des opérations UserManager dans votre méthode d'amorçage, cette modification n'inclura pas les erreurs de validation dans la sortie, vous devrez remplacer les méthodes DBContext SaveChanges, SaveChangesAsync et SaveChangesAsync (CT) selon la réponse @jwize.
Carl
115

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

Si vous regardez la définition de classe pour votre DbContext, ce sera quelque chose comme ceci:

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

L'idée générale avec des classes partielles - avez-vous remarqué que 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 remplacer la méthode SaveChanges à partir d'une classe partielle qui ajoute au DbContext .

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

Voilà ce que je ferais ( NOTE la différence est que je viens de passer outre la méthode SaveChanges dans la notre propre auteur DbContext classe partielle, pas CELUI GÉNÉRÉ ). Assurez-vous également que votre classe partielle utilise l' espace de noms correct, sinon vous vous cognerez 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
        }
    }
}
jwize
la source
Tu es un génie ...!
Florian F.
Excellente solution. Les gens devraient lire toutes les réponses avant de voter.
Guilherme de Jesus Santos
3
Vous devez également remplacer SaveChangesAsync et SaveChangesAsync (CancellationToken) - du moins c'est le cas avec le code en premier, pas sûr du modèle / db en premier.
Carl
@jwize. votre réponse m'a aidé dans ma base de données de la première modélisation des problèmes de gestion des exceptions. bonne réponse
3355307
1
Lors de l'utilisation de CodeFirst, le DbContext n'est évidemment pas généré. Cependant, lorsque vous utilisez le concepteur, les classes DbContext et Entity sont générées et doivent être remplacées à l'aide d'une classe partielle.
jwize
35

J'ai converti la réponse de Richard 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 ceci:

context.SaveChangesWithErrors();
Gordo
la source
4

J'ai converti la version de craigvl en C #, j'ai dû ajouter context.SaveChanges (); pour que cela 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());
}
Ben Pretorius
la source
3

Richard merci de m'avoir mis sur le bon chemin (a eu le même problème) ci-dessous est une alternative sans le wrapper qui a fonctionné pour moi dans la méthode de départ de configuration de 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

Était alors en mesure de voir l'exception dans la console du gestionnaire de packages. J'espère que cela aide quelqu'un.

craigvl
la source
-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
la source
1
Ce serait bien lorsque vous formatez votre code de sorte que votre texte ne soit pas à l'intérieur d'un bloc de code.
jmattheis
c'est probablement la pire réponse de stackoverflow formatée que j'ai jamais vue
crazy_crank