ChatGPT解决这个技术问题 Extra ChatGPT

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

我为此不知所措:

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

现在当我做

Add-Migration "remigrate" ; Update-Database;

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

我的 Seed() 方法中有一个断点,但是因为我在项目未运行时在控制台上运行它,所以我不知道如何获取详细信息(PS - 我已经看到了线程 {1 } 这显示了我如何看到该属性。)

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

快速更新:我通过系统地跟踪我的方法中的每个变量来解决我的问题,直到找到导致错误的原因。但是,我仍然想知道我的问题的答案,因为这样会快得多!
我认为您可以以编程方式运行迁移,然后捕获异常并迭代错误。这并不理想,但可以为您提供所需的详细信息。
当错误的答案排在答案的首位并获得所有荣誉时,令人沮丧。 StackOverflow 明显不足的地方!
如果您使用 Entity Framework,您可以查看我在 Solution for “Validation failed for one or more entities. See 'EntityValidationErrors' property for more details 上的回答。希望这可以帮助...

T
Troy Alford

我最近也被这个惹恼了。我通过在 Seed 方法的 Configuration 类中放置一个包装函数来修复它,并将对 SaveChanges 的调用替换为对我的函数的调用。此函数将简单地枚举 EntityValidationErrors 集合中的错误,并在 Exception 消息列出各个问题时重新引发异常。这使得输出显示在 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)


理查德,终于!有想法的人。一旦我尝试,我会回到这个问题。
这真的有助于追踪讨厌的东西:)
我使用了这种技术,但在上下文中使用了 savechanges 的覆盖。 public override int SaveChanges() 在上下文中。
正如我在下面回答的那样,使用部分类更有效。
如果您在种子方法中执行 UserManager 操作,那么此更改将不会在输出中包含验证错误,您需要根据 @jwize 答案覆盖 DBContext SaveChanges、SaveChangesAsync 和 SaveChangesAsync(CT) 方法。
j
jwize

已经使用部分类定义扩展您的 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 } 

部分类的整个想法——你是否注意到 DbContext 是一个部分类——是你可以扩展一个已经生成的类(或将类组织到多个文件中),在我们的例子中,我们还想覆盖 SaveChanges 方法从添加到 DbContext 的部分类中。

通过这种方式,我们可以从所有现有的 DbContext/SaveChanges 调用中获取错误调试信息,而无需更改您的种子代码或开发代码。

这就是我要做的(注意不同之处在于我只是在我们自己编写的 DbContext 部分类中覆盖 SaveChanges 方法,而不是生成的方法)。此外,请确保您的部分类使用正确的命名空间,否则您将撞到墙上。

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

你是个天才 ...!
很好的解决方案。人们应该在投票之前阅读所有答案。
您还应该覆盖 SaveChangesAsync 和 SaveChangesAsync(CancellationToken) - 至少代码优先是这种情况,首先不确定模型/数据库。
@jwize,不知道可以自动生成 DbContext 文件。但是你可以在一个完整的类中使用 try/catch 例程,如果它没有生成的话。我有。
使用 CodeFirst 时,显然不会生成 DbContext。但是,当您使用设计器时,会生成 DbContext 和 Entity 类,并且必须使用部分类覆盖。
G
Gordo

我将理查兹的答案转换为扩展方法:

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

R
Richard

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

c
craigvl

理查德感谢您让我走上正确的道路(有同样的问题),下面是一个没有包装器的替代方案,这在迁移配置种子方法中对我有用:

 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

然后能够在包管理器控制台中看到异常。希望这可以帮助某人。


M
Muhammad Usama

我也有同样的模型验证问题,但经过深思熟虑后自己成功地抓住了;我使用逆向工程方法从 80 多个模型类中找出问题; 1>复制dbcontext,更改名称(我在末尾添加“1”并在类构造函数和初始化等中进行相应更改。旧:>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> 对 Codefirst 进行更改将配置从旧 DbContext 迁移到我的新 Context。> 内部密封类配置:> DbMigrationsConfiguration { public Configuration() { > AutomaticMigrationsEnabled = false; } protected override void > Seed(DAL.AppDb1 context) {` 3>注释新 DbContext 中的 Dbsets 是有疑问的。 4> 如果成功,则在注释部分中应用更新迁移。 5> 如果不是,则 com mented 部分没有清除错误。 6>重复(4)直到找到正确的bug位置。 7> 快乐编码


当您格式化代码以使您的文本不在代码块内时会很好。
这可能是我见过的格式最差的stackoverflow答案