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

72090 次浏览

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

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

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

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

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