64

我有一个使用 Entity Framework 4.3 和代码优先方法的 ASP.NET MVC3 项目。我使用迁移来使数据库保持最新。

该项目受源代码控制,我有许多分支机构。我刚刚意识到的是,当我想将我的一个分支合并到 master 时会出现问题。由于我在两个分支中都创建了迁移文件,所以当我合并时会有重叠的迁移,这可能会导致冲突。

有没有一种好方法来管理具有多个分支的项目中的迁移?

更新

一种方法是合并,然后删除在分支分离时创建的所有迁移文件,然后创建一个新的迁移文件,其中包含从创建分支到重新合并的所有更改。这适用于开发环境,您可以在其中转储数据库并使用所有迁移文件重新构建它。那么问题将是生活环境。由于您无法回滚到创建分支的时间而不会有丢失数据的风险,因此当您尝试使用新的迁移文件更新实时数据库时会发生冲突。

4

7 回答 7

19

编辑:我的一位同事发现这样做更容易,为了完整起见,我将原始答案留在了底部。

(非常重要)实时环境中的迁移不能与当前分支中的迁移冲突,否则您需要重做所有迁移并手动解决数据模型更改冲突。

  1. 使用实时环境数据恢复您的开发数据库
  2. run update-database,它应该从您的分支运行迁移,并抱怨“无法更新数据库以匹配当前模型等等等等。”
  3. 运行add-migration MergeBranchBToMaster -ignoreChanges,这将创建一个空迁移。
  4. update-database再次运行
  5. 推动你的改变

第 3 步中的魔法基本上告诉 EF 关闭不匹配的模型,因此请确保您的迁移不会与实际环境中的迁移发生冲突。如果他们这样做了,您总是可以创建 SQL 脚本来推送丢失的迁移(这实际上是首选方法)。

原始答案

根据@Ladislav Mrnka 的回答,我找到了一个相当直接的解决方案。这将适用于实时环境[1],您只需注意不要更改任何已部署的迁移。

  1. 在合并之前,记下您添加的迁移 (MyMigration) 及其之前的迁移 (BaseMigration)

  2. 在 git 中合并分支

  3. 打开包管理器控制台,然后运行:UPDATE-DATABASE -TargetMigration:BaseMigration。这会将您的数据库恢复到应用任何冲突迁移之前的状态

  4. 删除本地迁移 (MyMigration)

  5. 运行:更新数据库。这将应用在其他分支中完成的所有较新的迁移。

  6. 运行:添加迁移 MyMigration。这将根据数据库的当前状态重新生成本地迁移,例如 git -rebase。

  7. 运行:更新数据库。使用本地迁移更新数据库。

如果您有多个本地迁移,这也适用,但它会将它们全部合并为一个。

[1] 通过使用实时环境,我的意思是生成的迁移可以应用于可能已经应用了一些/所有其他分支迁移的实时环境。这些步骤本身纯粹是出于开发目的。

于 2013-09-17T19:58:41.577 回答
19

在类似问题上处理实体框架迁移合并冲突有一个更好的解决方案。

合并后您需要做的就是在目标分支中重新构建迁移的元数据。那就是你不重新构建向上/向下代码,只是 resx 文件中的状态。

add-migration [the_migration_to_rescaffold_metadata_for]

这几乎总是有效的。如果合并中的不同迁移以迁移不再可运行或产生意外结果的方式更改了数据库,则该过程将失败。话虽如此 - 我认为这是一种非常罕见的情况,因为大多数迁移应该是自动生成的,或者至少不依赖于迁移本身也没有更改的其他表。

重新脚手架状态失败的一种情况可能是:

  • 列 foo 是一个 int 并且行包含 [0, 1, 2]

  • 从分支 A 迁移 A 将 foo 更改为布尔值(0 将自动变为假,> 0 将变为真)

  • 从分支 B 迁移 B 将 foo 更改为字符串。它希望它是一个 int 但它是一个布尔值,但迁移会成功。数据将丢失,因为当迁移 B 创建时,行将包含 ["0", "1", "2"]。当迁移 A 将列更改为布尔值(并且成功完成并获得预期结果)时,行现在将包含 ["0", "1", "1"] 并且迁移 B 的最终结果将与在B支线。

可能有更多的边缘情况,解决方案可能会出错。但是,如果向上/向下迁移代码不依赖于合并中另一个迁移更改的内容,那么只需更新迁移中的元数据就可以了。

于 2013-09-09T13:05:02.330 回答
13

合并迁移是恕我直言的手动任务。部分迁移代码是自动生成的,我们通常不会合并自动生成的代码——而是在合并后再次运行自动生成。

在 ADO.NET 团队提供一些建议之前,我会遵循简单的原则:

  • 在进行合并之前,将主数据库恢复到分支之前使用的版本
  • 合并你的分支
  • 排除从合并程序集分支后创建的迁移类
  • 为合并的代码库添加一个新的迁移,它将在合并分支之后将您的数据库迁移到分支之前的状态
  • 如果您排除的迁移类包含一些自定义,请将它们合并到新的迁移类
  • 运行迁移以将数据库迁移到当前合并版本

如果您的分支包含多个迁移步骤(版本),您将丢失它们并以两个版本结束 - 分支之前和合并之后。

编辑:

它不会在现场环境中工作。这里的问题是开发过程本身。如果您有实时环境,则应保持其分支不变(小错误修复除外)。如果您继续在该分支中使用生产部署进行开发,同时您在没有持续集成的情况下在单独的分支中构建另一个版本(= 持续将更改合并回主分支以将您的新开发与主代码库集成)问题。我认为迁移通常无法处理这个问题。

在这种情况下,唯一的选择可能是从合并的解决方案中删除所有迁移并MigrationHistory从数据库中删除表。然后您可以再次在项目上启用迁移并添加初始迁移以使用当前数据库作为起点=无法回到以前的版本,因为不存在有关以前迁移的信息。

于 2012-05-12T11:11:55.843 回答
11

Rowan Miller 在第 9 频道制作了一个关于这个主题的精彩视频:迁移 - 团队环境。它指的是实体框架 6。

它描述了第一个开发人员 A 和 B 正在开发相同的模型并且 A 先签入的场景。现在开发者 B 必须处理他从 A 获得最新版本时遇到的问题。

这与不同分支之间的冲突本质上是相同的,因为一般问题是合并迁移更改同时完成,但实际上具有不同的模型源状态。

解决方案是:

  • 在解决版本控制系统的冲突时,开发者 B 必须同时接受来自自己和开发者 A 的更改。
  • 此时开发人员 B的UpdateDatabase命令仍然会失败(错误消息:“无法更新数据库以匹配当前模型,因为有待处理的更改......”
  • IgnoreChanges开发人员 B 必须使用以下选项创建“空迁移” :

Add-Migration NameOfMigration -IgnoreChanges

然后UpdateDatabase命令将成功。


问题的根源

更新数据库时发生错误的根源是因为 EF 将迁移引用的模型的快照存储在迁移文件中的 resx 文件中。

在这种情况下,开发人员 B 的“当前模型”快照在获取/合并开发人员 A 所做的更改后不正确。

于 2015-11-19T09:16:45.967 回答
4

我对此进行了一些思考,我希望我能为这里提出的不同观点和实践做出贡献。

考虑一下您的本地迁移实际代表什么。在本地使用开发数据库时,我在向表中添加列等、添加新实体等时,使用迁移以最方便的方式更新数据库。

因此,Add-Migration 将当前的模型(我们称之为模型 b)与之前的模型(模型 a)进行对比,并生成从数据库中的 a => b 开始的迁移。

对我来说,如果每个人都确实拥有自己的数据库并且组织中存在某种阶段/测试/开发/生产数据库服务器,那么尝试将我的迁移与其他任何人的迁移合并是没有意义的。这一切都取决于团队是如何设置的,但是如果您想真正以分布式方式工作,那么将彼此与其他人所做的更改隔离开来是有意义的。

好吧,如果你在分布式工作并且有一些实体,例如,你工作的人。出于某种原因,许多其他人也在研究它。因此,您可以根据 sprint 中特定故事的需要添加和删除 Person 上的属性(我们在这里都在灵活地工作,不是吗?),例如您首先将社会安全号码转换为整数,因为您不是那个明亮,然后到一个字符串等。

您添加名字和姓氏。

然后你就完成了,你有十个奇怪的上下迁移(你可能在工作时删除了其中一些,因为它们只是垃圾),你从中央 Git 存储库获取一些更改。哇。你的同事鲍勃也需要一些名字,也许你们应该互相谈谈?

无论如何,他已经添加了 NameFirst 和 NameLast,我猜……那你怎么办?好吧,您合并、重构、更改,使其具有更合理的名称……例如 FirstName 和 LastName,您运行测试并检查他的代码,然后推送到中央。

但是迁移呢?好吧,现在是时候进行迁移中央仓库了,或者更具体地说,分支“测试”包含从其模型 a => 模型 b 的一个不错的小迁移。这次迁移将是一次且只有一次迁移,而不是十次奇怪的迁移。

你明白我在说什么吗?我们正在使用漂亮的小 pocos,它们的比较构成了实际的迁移。所以,我们根本不应该合并迁移,在我看来,我们应该有每个分支的迁移或类似的东西。

事实上,我们甚至需要在合并之后在分支中创建迁移吗?是的,如果这个数据库是自动更新的,我们需要。

必须再努力一些,至少这些是我对此的想法。

于 2013-12-12T09:54:03.143 回答
2

考虑使用不会导致这些冲突的其他迁移库,例如 FluentMigrator 或 Migrator.NET。

我不认为 EF 迁移真的准备好用于分支和合并的一般用途 - 这是很多工作,而且很容易犯令人讨厌的错误。

于 2013-02-19T13:51:13.563 回答
0

我认为@LavaEater 所说的很有意义。我正在实施一个分支策略(开发、主要、发布)并将其与开发、质量保证和发布过程中的环境保持一致。

  • 开发分支 - 本地开发
  • 主分支 - 合并来自开发分支的更改并部署到我的暂存环境(Azure 网站和 SQL 数据库)
  • 发布分支 - 合并来自 Main 的更改并部署到生产环境(另一个 Azure 网站和 SQL 数据库)

我遇到了上面讨论的问题,在我看来,迁移的复杂性和潜在的变通办法给发布过程带来了很大的风险。在 Development、Main 和 Release 中有效地执行独立迁移意味着我在 Dev 中的构建中包含的模式不是进入 QA 的 Staging 模式,QA 签署的 Staging 模式不是部署到 Live 的模式(除非我遵循我确信可行但可能容易出错的建议解决方案之一)。

回应@LavaEater - 我首先从EF代码中获得的真正好处是什么?就个人而言,我认为我可以轻松地从代码生成模式(如果我愿意,可能会调整自动生成的迁移)。在那之后,迁移是一个简单的部署过程的复杂化。

我目前的想法是首先使用代码在开发中生成迁移,然后:-

  • 选项 A) - 使用 Update-Database -script 编写架构更改脚本并将它们置于源代码控制之下。如果 2 个人修改同一个模型,仍然存在一些潜在的冲突,但我认为这更容易管理。

  • 选项 B) - 使用 SQL Compare 之类的东西来生成架构更改脚本。这可能会更加灵活和透明,因为我想确切地看到我正在应用到我的生产数据库的架构更改(称我为偏执狂)。

我错过了什么吗?我想会有一些配置来禁用 Main 和 Release 分支中的代码优先迁移(假设数据库将由脚本创建和更新)。除此之外,这感觉像是一个安全的解决方案,但我会重视第二个意见。

于 2014-03-08T14:36:11.047 回答