ChatGPT解决这个技术问题 Extra ChatGPT

EF Code First: How do I see 'EntityValidationErrors' property from the nuget package console?

I'm at a loss for this:

I've defined my classes for a entity framework (4.1.3) code first approach. Everything was fine (I was creating the tables etc.) until I started to Seed.

Now when I do the

Add-Migration "remigrate" ; Update-Database;

I get an error on the package console "Validation failed for one or more entities. See 'EntityValidationErrors' property for more details."

I have a breakpoint in my Seed() method but because I'm running this on the console when the project is not running, I'm clueless as to how to get to the details (PS - I've seen the thread Validation failed for one or more entities while saving changes to SQL Server Database using Entity Framework which shows how I can see the property.)

I know that my Seed() method has a problem because if I put a return right after the method call, the error goes away. So how do I set my breakpoint so I can see what the validation error is? Kinda lost. Or is there some other way to trace it in the nuget console??

Quick update: I solved my problem by systematically tracking each variable within my method until I found what was causing the error. However, I'd still like to know the answer to my question as that would be much faster!
I think you could run the migration programmatically and then catch the exception and iterate on the errors. It's not ideal but could give you the details you need.
Frustrating when the wrong answer is at the top of the answers and gets all the credit. A place where StackOverflow clearly falls short!
If you use Entity Framework you can have a look at my answer on Solution for “Validation failed for one or more entities. See 'EntityValidationErrors' property for more details. Hope this helps...

T
Troy Alford

I got annoyed by this recently too. I fixed it by putting a wrapper function in the Configuration class in the Seed method, and replaced calls to SaveChanges with calls to my function instead. This function would simply enumerate the errors within the EntityValidationErrors collection, and rethrow an exception where the Exception message lists the individual problems. This makes the output show up in the NuGet package manager console.

Code follows:

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

Just replace calls to context.SaveChanges() with SaveChanges(context) in your seed method.


Richard, finally! Someone with an idea. I will come back to this question once I try it.
This really helps tracking down the nasties :)
I used this technique but used an override of savechanges inside the context instead. public override int SaveChanges() inside the context.
It is more effective using partial classes as I have answered below.
If you are doing UserManager operations in your seed method then this change won't include the validation errors in the output, you would need to override the DBContext SaveChanges, SaveChangesAsync and SaveChangesAsync(CT) methods as per @jwize answer.
j
jwize

Extend Your DBContext Class Already With a Partial Class Definition!

If you look at the class definition for your DbContext it will be something like the following:

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

So, in another file you can create the same definition and override the parts you want to.

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

The whole idea with partial classes --did you notice the DbContext is a partial class-- is that you can extend a class that has been generated (or organize classes into multiple files) and in our case we also want to override the SaveChanges method from within a partial class that adds to the DbContext.

This way we can get error debugging information from all existing DbContext/SaveChanges calls everywhere and not have to change your Seed code or development code at all.

This is what I would do (NOTE the difference is that I just override SaveChanges method in the our own authored DbContext partial class, NOT THE GENERATED ONE). Also, make sure you partial class uses the correct namespace or you will be banging your head against the wall.

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

You are a genius ...!
Great solution. People should read all the answers before upvoting.
You should also override SaveChangesAsync and SaveChangesAsync(CancellationToken) too - at least that is the case with code first, not sure about model/db first.
@jwize, didn't know that DbContext file can be auto-generated. But you can use the try/catch routine in a full class, if it's not generated. I have.
When using CodeFirst the DbContext is obviously not generated. However, when you use the designer the DbContext and Entity classes are generated and must be overridden using a partial class.
G
Gordo

I converted Richards answer to an extension method :

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

Call like this:

context.SaveChangesWithErrors();

R
Richard

I converted craigvl's version to C# I had to add context.SaveChanges(); in order for it to work for me as below.

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

c
craigvl

Richard thanks for getting me on the right path (had same issue) below is an alternative without the wrapper this worked for me in the migration configuration seed method:

 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

Was then able to see the exception in the package manager console. Hope this helps someone.


M
Muhammad Usama

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 > > { > public AppDb(): base("DefaultConnection", throwIfV1Schema: false) > { > > } > > public static AppDb Create() >{ >return new AppDb(); >} **New:** >public class AppDb1 : IdentityDbContext >{ >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 { 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


Would be nice when you format your code that your text is not inside a code block.
this is probably the worst formatted stackoverflow answer I've ever seen