【问题标题】:Update-Database fails due to Pending Changes, but Add-Migration Creates a Duplicate MigrationUpdate-Database 由于挂起的更改而失败,但 Add-Migration 创建了重复的迁移
【发布时间】:2013-07-29 10:38:47
【问题描述】:

我正在使用 Entity Framework 5.0 Code First 迁移,并且在运行 Update-Database 时遇到问题。它说有待定的模型更改;但它应该是最新的,所以我运行

Add-Migration SomeMigrationName

它会创建一个文件...但是,它会创建一个与先前迁移基本相同的文件(如果我尝试在该文件上再次更新数据库,它会因与尝试删除相关的问题而失败一个不存在的约束)。此外,我已经能够确认“原始”迁移已基于数据库中的数据模型以及 __MigrationHistory 表中存在的记录运行!

如果我删除整个数据库,然后自动或手动再次运行所有迁移,我会遇到同样的问题。

我拥有的“原始”迁移文件如下:

public partial class RenameLinkColumns : DbMigration
{
    public override void Up()
    {
        DropForeignKey("dbo.Listing", "OfferedByUserId", "dbo.User");
        DropIndex("dbo.Listing", new[] { "OfferedByUserId" });
        AddColumn("dbo.Listing", "ListedByUserId", c => c.Int(nullable: false));
        AddForeignKey("dbo.Listing", "ListedByUserId", "dbo.User", "UserId", cascadeDelete: true);
        CreateIndex("dbo.Listing", "ListedByUserId");
        DropColumn("dbo.Listing", "OfferedByUserId");
    }

    public override void Down()
    {
        AddColumn("dbo.Listing", "OfferedByUserId", c => c.Int(nullable: false));
        DropIndex("dbo.Listing", new[] { "ListedByUserId" });
        DropForeignKey("dbo.Listing", "ListedByUserId", "dbo.User");
        DropColumn("dbo.Listing", "ListedByUserId");
        CreateIndex("dbo.Listing", "OfferedByUserId");
        AddForeignKey("dbo.Listing", "OfferedByUserId", "dbo.User", "UserId", cascadeDelete: true);
    }
}

当我再次运行 Add-Migration 时,该文件中的 Up/Down 方法与这些完全相同。

迁移能够正确检测到我重命名了 ForeignKey 列,这让我印象深刻;但这就是导致窒息的原因吗?

似乎有一种解决方法:我已删除数据库和所有迁移文件,并创建了一个新的“初始”迁移,但如果可能的话我不希望这样做。

更新:不是导致此问题的最新迁移,但问题在合并后开始(我独自工作,但正在模拟分支上的团队工作了解更多关于 git 的信息),并尝试使数据库与合并同步。这可能是由于在合并后以某种特定的顺序放置迁移造成的 - 尽管已注意到,当我给它们一个空 DB 时,迁移确实按照它们运行的​​顺序按预期工作。

此外,当表中有数据时,这种原始迁移需要手动调整,因为需要将数据从旧列复制到新列。但是,我在对该文件进行手动编辑和未手动编辑的情况下对该文件进行了测试,但仍然遇到了上述行为。

【问题讨论】:

    标签: c# entity-framework entity-framework-migrations


    【解决方案1】:

    This answer explains why it happens。为了解决它,我调用add-migration 并将其命名为MERGE,然后删除任何已经发生的重复迁移代码。这只是为了更新模型快照以反映合并后的模型。

    例子:

    public partial class MERGE : DbMigration
    {
        public override void Up()
        {
            // Intentionally left blank.
    
            // This may seem like a hack, but it is necessary when using source control.
            // When a migration is created via add-migration, EF creates 
            // an .edmx file from the current code first classes. It compares this .edmx to the .edmx stored in the last migration before this, 
            // which I'll call it's parent migration. The edmx snapshots are gzipped and stored in base64 in the resource files (.resx) if you 
            // want to see them. EF uses the difference between these two snapshots to determine what needs to be migrated.
    
            // When using source control it will happen that two users add entities to the model independently. The generated edmx snapshots will 
            // only have the changes that they have made. When they merge in source control, they will end up with this:
    
            // Migration                        |  Snapshot Contents
            // -------------------------------- | ----------------
            // 20150101_Parent Migration        |  A
            // 20150102_Developer 1's Migration |  A + Change 1
            // 20150103_Developer 2's Migration |  A + Change 2
    
            // So calling add-migration will create the current snapshot edmx from the Code First model and compare it to the 
            // the latest migration's snapshot, which is A + Change 2, and see that Change 1 is missing. That is why it 
            // creates a duplicate migration. We know that the migrations have already been applied, so the only thing that this 
            // migration will do is update the current snapshot .edmx so that later migrations work fine.
        }
    
        public override void Down()
        {
    
        }
    }
    

    【讨论】:

    • 嗨,迈克,由于我的开发环境早已不复存在,我无法交叉检查您的回复,但接受您作为答案,因为您提供了令人信服的推理(以及上面的一个很好的迁移评论 :)
    • 感谢您对此的解释。我已经将其作为“修复”来解决,但在不了解它为什么起作用的情况下不高兴检查它。
    • 这行得通,但太可怕了,每次我有一个小改动时,我都没有什么做的迁移
    • This MSDN post 也很好地解释了为什么会出现问题以及如何处理它的不同选项。
    【解决方案2】:

    我也经常看到这个。 我不知道为什么,希望我这样做了,但我的解决方案是进行添加迁移,这将产生重复。 现在这个副本在编辑器中打开,然后我编辑它,所以 Up 和 Down 方法是空的。 所以结果是一个什么都不做的迁移文件! VS 很高兴,您可以毫无错误地更新数据库(直到下一次)。

    我希望这会有所帮助:)

    【讨论】:

      【解决方案3】:

      作为对这部分问题的一般回答:

      (我一个人工作,但正在模拟分支上的团队工作来学习 更多关于 git 的信息),并尝试使数据库与 合并。这可能是由于将迁移放置在某些 合并后的特定顺序

      是的,很可能就是这种情况,EF 在合并后很容易混淆,但可以解决它。关键是首先要了解它为什么会感到困惑:

      从分支合并迁移时会发生什么?

      EF 感到困惑的原因是 EF 将数据库的当前形状存储在实际迁移文件中,它是每次迁移下找到的 resx 文件中的“目标”值,例如

      假设你有两个分支:

      • 分支 1:您将“URL”字段添加到博客表。目标字段现在包含具有该额外字段的数据库的描述
      • 分支 2:您添加了一个新的“链接”表。同样,Target 字段中的 db 描述现在有那个额外的表,但它没有 URL 字段,因为它是在不同的分支上添加的

      如果您现在将这两个分支合并回主分支,然后尝试运行迁移,您可能会遇到可怕的情况

      无法更新数据库以匹配当前模型,因为有 挂起的更改和自动迁移被禁用...

      该错误消息确实是无益的误导,但错误的原因实际上很容易理解:

      为什么多个分支会混淆 EF?

      当两个分支合并回 Master 时,EF 认为它们中的最后一个迁移(根据文件名开头的日期)具有该迁移目标中数据库的真实当前状态字段。

      但是,正如我们在上面看到的,分支 1 和分支 2 对数据库的真实状态有不同的看法(一个认为有一个新的 URL 字段,另一个认为有一个新的链接字段),而且他们无济于事现在错了,因为数据库现在有这两个字段。

      出现错误消息是因为 EF 根据迁移中的实际步骤计算 DB 的预期状态,并将其与 Target 进行比较,发现它们不同。

      如何解决

      解决所有这些问题的方法是强制 EF 根据项目中现在的所有迁移重新计算数据库的状态,然后将 Target 值更新为包含对 all所做的更改的值> 迁移。

      最简单的方法是添加一个“空白”迁移,使用以下命令:

      Add-Migration <pick_a_name> –IgnoreChanges

      另一种方法是在最终迁移中覆盖目标值。

      查阅手册..

      以上所有内容都是对了解一般迁移以及在团队环境中的出色指南的简要概述,可在以下位置找到:

      Microsoft's Code First Migrations in Team Environments

      应该在每条 EF 错误消息中引用该文档,因为它对如此多的日常 EF 问题有意义。

      【讨论】:

      • 感谢您的精彩解释! @tomRedox
      【解决方案4】:

      我刚刚遇到了同样的问题。

      一旦创建迁移。我尝试更新数据库并收到以下消息:

      无法更新数据库以匹配当前模型,因为有 挂起的更改和自动迁移被禁用。要么写 待定模型更改为基于代码的迁移或启用自动 移民。将 DbMigrationsConfiguration.AutomaticMigrationsEnabled 设置为 true 启用自动迁移。您可以使用添加迁移 命令将挂起的模型更改写入基于代码的迁移。

      然后,我再次生成了迁移,但它被复制了。

      我在创建迁移后构建项目时解决了这个问题。然后 Update-Database 脚本找到迁移方法并且它可以工作。至少对我而言。

      【讨论】:

        【解决方案5】:

        这不是解决方案,它总是有帮助的。我也会推荐所有答案。

        我遇到了同样的问题,Mike 的建议不适用于我的情况。我发现了为什么...

        带有迁移DLL(共享点)部署的一部分,并且也在GAC(C:\Windows\Microsoft. NET\程序集\GAC_MSIL)。

        我从 GAC 中删除了 DLL 并重新启动了 Visual Studio。

        现在“更新数据库”采用了正确的 DLL,并且迁移工作正常。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-10-08
          • 2014-12-01
          • 1970-01-01
          • 2018-08-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-27
          相关资源
          最近更新 更多