EF代码优先:如何从nuget软件包控制台中看到“ EntityValidationErrors”属性?


127

我为此感到茫然:

我已经为实体框架(4.1.3)代码优先方法定义了我的类。一切都很好(我正在创建表等),直到我开始播种为止。

现在当我做

Add-Migration "remigrate" ; Update-Database;

我在程序包控制台上收到一个错误“对一个或多个实体的验证失败。有关更多详细信息,请参见'EntityValidationErrors'属性。”

我的Seed()方法中有一个断点,但是因为当项目未运行时我正在控制台上运行此断点,所以对于如何获得详细信息一无所知(PS-我已经看到线程验证失败了一个或多个实体,同时使用实体框架将更改保存到SQL Server数据库中,该框架显示了我如何看到该属性。)

我知道我的Seed()方法有问题,因为如果我在方法调用后立即返回,错误就会消失。那么,如何设置断点,以便查看验证错误是什么?金田迷路了。还是有其他方法可以在nuget控制台中对其进行跟踪?


快速更新:我通过系统地跟踪方法中的每个变量来解决问题,直到找到导致错误的原因。但是,我仍然想知道我的问题的答案,因为那样会更快!
jeremy 2012年

我认为您可以以编程方式运行迁移,然后捕获异常并迭代错误。这不是理想的选择,但可以为您提供所需的详细信息。
Pawel 2012年

当错误的答案位于答案的顶部时,您会感到沮丧,并获得所有荣誉。StackOverflow明显不足的地方!
jwize 2014年

Answers:


216

最近我也为此感到恼火。我通过将包装函数放在Seed方法的Configuration类中来解决此问题,并用SaveChanges对函数的调用替换了对的调用。该函数将简单地枚举EntityValidationErrors集合中的错误,并在抛出异常消息列出单个问题的地方抛出异常。这会使输出显示在NuGet程序包管理器控制台中。

代码如下:

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

只需更换调用context.SaveChanges()SaveChanges(context)在你的种子的方法。


理查德,终于!有人有主意。一旦尝试,我将回到这个问题。
杰里米

这确实有助于查明病态:)
阿姆(Eminem)

3
我使用了这种技术,但是在上下文内部使用了savechanges的替代。public override int SaveChanges() 在上下文中。
克里斯滕·格里德

5
正如我在下面回答的那样,使用局部类会更有效。
jwize

1
如果您在种子方法中执行UserManager操作,那么此更改将不会在输出中包含验证错误,您需要根据@jwize答案覆盖DBContext SaveChanges,SaveChangesAsync和SaveChangesAsync(CT)方法。
卡尔

115

已经使用部分类定义扩展了DBContext类!

如果您查看DbContext的类定义,它将类似于以下内容:

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

因此,在另一个文件中,您可以创建相同的定义并覆盖所需的零件。

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

有部分类整体思路--did你注意到的DbContext是部分分类-是你可以扩展已生成的类(或类组织成多个文件),并在我们的情况下,我们也希望覆盖的SaveChanges方法从添加到DbContext的局部类中。

这样,我们可以从任何地方的所有现有DbContext / SaveChanges调用中获取错误调试信息,而不必完全更改您的Seed代码或开发代码。

这就是我要做的(注意,区别是我只是重写了我们自己编写的DbContext子类中的SaveChanges方法,而不是GENERATED ONE)。另外,请确保您的局部类使用正确的命名空间,否则您将无法正常工作。

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

你是个天才 ...!
Florian F.

很好的解决方案。人们应该在投票之前先阅读所有答案。
Guilherme de Jesus Santos

3
您还应该重写SaveChangesAsync和SaveChangesAsync(CancellationToken)-至少在代码优先的情况下,而不是模型/数据库优先。
卡尔

@jwize。您的回答对我的数据库建模异常处理问题很有帮助。很好的答案
3355307

1
使用CodeFirst时,显然不会生成DbContext。但是,当您使用设计器时,将生成DbContext和Entity类,并且必须使用部分类将其重写。
jwize

35

我将Richards的答案转换为扩展方法:

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

像这样打电话:

context.SaveChangesWithErrors();

4

我将craigvl的版本转换为C#,我不得不添加context.SaveChanges();。为了使其对我有效,如下所示。

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)感谢您使我走上正确的道路(有同样的问题),这是一种替代方法,而没有在迁移配置种子方法中为我工作的包装器:

 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

这样便可以在程序包管理器控制台中看到异常。希望这对某人有帮助。


-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
当您格式化代码以使文本不在代码块内时,效果会很好。
jmattheis

这可能是我见过的最糟糕的格式化stackoverflow答案
crazy_crank
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.