如何在具有多个分支的项目中管理迁移?

时间:2012-05-11 17:59:36

标签: c# asp.net-mvc-3 version-control entity-framework-4.3 ef-migrations

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

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

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

更新

一种方法是合并,然后删除在分支分离时创建的所有迁移文件,然后创建一个新的迁移文件,该文件保存从创建分支到重新合并之前的所有更改。这将为dev-environment工作,您可以在其中转储数据库并使用所有迁移文件重新构建它。那么问题就是现实环境。由于您无法回滚到创建分支的时间而没有丢失数据的风险,因此当您尝试使用新的迁移文件来更新实时数据库时会发生冲突。

7 个答案:

答案 0 :(得分:17)

我认为接受的答案是不正确的。在类似问题上有一个much better solution for handling entity framework migration merge conflicts

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

add-migration [the_migration_to_rescaffold_metadata_for]

如果合并中的其他迁移更改了数据库,导致迁移不再可运行或产生意外结果,则此过程将失败。话虽这么说 - 我相信这是一个非常罕见的情况,因为大多数迁移应该是自动生成的,或者至少不依赖于迁移本身没有改变的其他表。非常小的问题,但需要注意的一点。

一个这样的案例可能是fxp(我想不出更好的例子)

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

  • 从分支A迁移A将foo更改为boolean(0将自动变为false并且> 0 将成为真实)

  • 从分支B迁移B将foo更改为字符串。它期望它是一个int,但它是一个布尔值,但迁移将成功。数据将丢失,因为创建迁移B时,行将包含[“0”,“1”,“2”]。当迁移A将列更改为布尔值(并且成功完成并且具有预期结果)时,行现在将包含[“0”,“1”,“1”],而迁移B将具有与在B部门。

可能存在更多边缘情况,解决方案可能出现问题。但是,如果迁移上/下代码不依赖于合并中另一个迁移所改变的内容,那么只应更新迁移中的元数据。

答案 1 :(得分:15)

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

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

  1. 使用实时环境数据恢复您的开发数据库
  2. 运行update-database,它应该从您的分支机构运行迁移,并抱怨“无法更新数据库以匹配当前模型等等等等。”
  3. 运行add-migration MergeBranchBToMaster -ignoreChanges,这将创建一个空迁移。
  4. 再次运行update-database
  5. 推送您的更改
  6. 步骤3中的魔术基本上告诉EF关闭不匹配的模型,因此非常确定您的迁移不会与实时环境中的迁移发生冲突。如果他们这样做,您始终可以创建SQL脚本来推送缺少的迁移(这实际上是首选方法)。

    原始答案

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

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

    2. 在git中合并分支

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

    4. 删除您的本地迁移(MyMigration)

    5. 运行:UPDATE-DATABASE。这将应用在其他分支中完成的所有较新迁移。

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

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

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

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

答案 2 :(得分:13)

合并迁移是IMHO手动任务。迁移代码的一部分是自动生成的,我们通常不合并自动生成的代码 - 而是在合并后再次运行自动生成。

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

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

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

编辑:

它不适用于实时环境。这里的问题是开发过程本身。如果你有实时环境,你应该保持其分支不受影响(除了小错误修复)。如果您继续使用生产部署在该分支中进行开发,同时在单独的分支中构建另一个版本而不进行持续集成(=将更改连续更改回主分支以将新开发与主代码库集成)问题。我认为一般的迁移都无法解决这个问题。

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

答案 3 :(得分:9)

Rowan Miller在第9频道制作了关于此主题的精彩视频:Migrations - Team Environments。 它指的是实体框架6。

它描述了第一个开发人员A和B正在使用相同模型而A先检查的情况。 现在,开发人员B必须处理他从A获得最新版本时遇到的问题。

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

解决方案是:

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

Add-Migration NameOfMigration -IgnoreChanges

然后UpdateDatabase命令将成功。


问题的根源

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

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

答案 4 :(得分:4)

我已经考虑过这一点,我希望我能为这里提出的不同意见和做法做出贡献。

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

因此,Add-Migration会针对我的以前的模型(模型a)检查我的当前模型(让我们称之为模型b)并生成从a =的迁移> b在数据库中。

对我而言,尝试将迁移与任何其他迁移合并都没有意义,如果每个人确实拥有自己的数据库,那么就存在某种阶段/ test / dev / production组织中的数据库服务器。这一切都取决于团队如何设置,但是如果你想真正以分布式方式工作,那么将彼此隔离以及其他人所做出的改变是有意义的。

好吧,如果你在分布式工作并拥有一些实体,例如,你工作的人。出于某种原因,许多其他人也在努力。那么,你可以根据需要在Sprint中添加和删除你的特定故事的属性(我们都在这里敏捷工作,不是吗?),就像你最初做成整数的社会安全号,因为你不是那个很亮,然后是一个字符串等。

您添加FirstName和LastName。

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

无论如何,他已经添加了NameFirst和NameLast,我猜......所以你做了什么?好吧,你合并,重构,改变所以它有更多合理的名字......比如FirstName和LastName,你运行测试并检查他的代码,然后你推到中心。

但迁移呢?好吧,现在是时候进行迁移移动中央仓库,或者更具体地说,分支“测试”包含一个很好的小迁移来自模型a =>模特b。这种迁移将是唯一一次迁移,而不是十次奇怪的迁移。

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

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

要做更多的工作,这至少是我对此的想法。

答案 5 :(得分:2)

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

我不认为EF迁移真的可以用于分支机构和合并 - 这是很多工作,太容易犯错误。

答案 6 :(得分:0)

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

  • 发展部门 - 本地发展
  • 主分支 - 从开发分支合并更改并部署到我的临时环境(Azure网站和SQL数据库)
  • 发布分支 - 将更改从Main合并到部署到生产环境(另一个Azure网站和SQL数据库)

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

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

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

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

  • 选项B) - 使用类似SQL Compare的方法生成模式更改脚本。这可能更加灵活和透明,因为我想确切地看到我正在应用于我的生产数据库的模式更改(称为偏执狂)。

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