32

我正在使用 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 的更多信息),并尝试使数据库与合并。这可能是由于在合并后以某种特定的顺序放置迁移造成的 - 尽管已注意到,当我给它们一个空数据库时,迁移确实按照它们运行的​​顺序工作。

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

4

5 回答 5

36

这个答案解释了为什么会发生。为了解决它,我调用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()
    {

    }
}
于 2015-01-15T11:36:05.513 回答
4

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

我希望这有帮助 :)

于 2013-08-15T12:06:48.337 回答
3

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

(我一个人工作,但正在模拟分支上的团队工作,以了解更多关于 git 的信息),并试图让数据库与合并同步。这可能是由于在合并后以某种特定顺序放置迁移造成的

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

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

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

解决方案资源管理器中显示子 .resx 文件的迁移文件

显示目标的迁移 resx 文件

假设你有两个分支:

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

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

无法更新数据库以匹配当前模型,因为存在待处理的更改并且禁用了自动迁移...

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

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

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

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

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

如何修复它

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

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

Add-Migration <pick_a_name> –IgnoreChanges

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

看说明书。。

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

Microsoft 在团队环境中的 Code First 迁移

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

于 2019-09-12T10:07:20.770 回答
0

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

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

无法更新数据库以匹配当前模型,因为存在待处理的更改并且自动迁移已禁用。将挂起的模型更改写入基于代码的迁移或启用自动迁移。将 DbMigrationsConfiguration.AutomaticMigrationsEnabled 设置为 true 以启用自动迁移。您可以使用 Add-Migration 命令将挂起的模型更改写入基于代码的迁移。

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

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

于 2014-06-24T13:56:34.763 回答
0

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

我有同样的问题,迈克的建议不适用于我的情况。我发现了为什么...

带有迁移的DLL是(共享点)部署的一部分,也在GAC(C:\Windows\Microsoft.NET\assembly\GAC_MSIL)中。

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

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

于 2019-08-29T07:48:13.673 回答