如何pipe理多个分支项目中的迁移?
我有一个ASP.NET MVC3项目使用entity framework4.3与代码优先的方法。 我使用Migrations来保持数据库是最新的。
该项目是在源代码控制下,我有一些分支机构。 我刚刚意识到的是,当我想将我的一个分支合并到主控中时,会出现问题。 由于我在两个分支都创build了迁移文件,所以当我合并时会有重叠的迁移,这可能会导致冲突。
有多个分支的项目中pipe理迁移的好方法吗?
更新
一种方法是合并,然后删除分支分离时创build的所有迁移文件,然后创build一个新的迁移文件,该文件保存从创build分支到合并为止的所有更改。这将适用于dev-environment,您可以在其中转储数据库并使用所有迁移文件重新构build它。 那么问题就是现场环境。 由于您无法回滚创build分支而没有丢失数据的风险,因此尝试使用新的迁移文件更新实时数据库时会有冲突。
我认为接受的答案是不正确的。 在类似的问题上处理entity framework迁移合并冲突有更好的解决scheme 。
合并之后您所需要做的就是在目标分支中重新构build迁移的元数据。 那就是你不要重新调整up / down代码,只是resx-file中的状态。
add-migration [the_migration_to_rescaffold_metadata_for]
如果合并中的其他迁移已经更改数据库,迁移不再可运行或出现意外结果,则此过程将失败。 这就是说 – 我相信这是一个非常罕见的情况,因为大多数迁移应该是自动生成的,或者至less不依赖其他迁移本身没有改变的表。 很小的问题,但要注意的一个问题。
一个这样的情况可能是fxp(我想不出一个更好的例子)
-
列foo是一个int,行包含[0,1,2]
-
从分支A迁移A将foo更改为布尔值(0将自动变为false,> 0将变为true)
-
分支B的迁移B将foo更改为string。 它期望它是一个整数,但它是一个布尔值,迁移将成功,虽然。 自迁移B创build时,数据将会丢失,行将包含[“0”,“1”,“2”]。 当迁移将列更改为布尔值(并且成功执行并具有预期的结果),行现在将包含[“0”,“1”,“1”],而迁移B将具有与所观察到的不同的最终结果分支B.
有可能更多的边缘情况下,解决scheme可能出现问题。 但是,如果迁移向上/向下代码不依赖于合并中的另一个迁移所改变的事情,它应该可以很好地更新迁移中的元数据。
合并迁移是恕我直言的手动任务。 部分迁移代码是自动生成的,我们通常不会合并自动生成的代码,而是在合并后再次运行自动生成代码。
在ADO.NET团队提供一些build议之前,我会遵循一个简单的原则:
- 在合并之前,将主数据库恢复为分支之前使用的版本
- 合并你的分支
- 排除从合并程序集分支之后创build的迁移类
- 为合并的代码库添加一个新的迁移,它将在分支之前的状态中将您的数据库迁移到合并分支之后的状态
- 如果排除的迁移类包含某些定制,则将它们合并到新的迁移类中
- 运行迁移将数据库迁移到当前合并版本
如果你的分支包含多个迁移步骤(版本),你将失去它们,你将以两个版本结束 – 分支之前和合并之后。
编辑:
它不会在现场环境中工作。 这里的问题是开发过程本身。 如果你有活的环境,你应该保持其分支不变(除了小错误修复)。 如果您继续在该分支进行生产部署的开发,并且同时在单独分支中构build另一个版本,而不进行持续集成(=连续合并更改回主分支以将新开发集成到主代码库)问题。 我认为移民一般不能解决这个问题。
在这种情况下,唯一的select可能是从合并解决scheme中删除所有迁移,并从数据库中删除MigrationHistory
表。 您可以再次启用对项目的迁移,并添加初始迁移以使用当前数据库作为起点=没有办法回到以前的版本,因为没有关于以前的迁移的信息将存在。
编辑:我的一个同事发现了一个更容易做到这一点,我留下了我的原始答案在底部完整性。
(非常重要)实时环境中的迁移不得与当前分支中的迁移冲突,否则需要手动重做所有迁移并解决数据模型更改冲突。
- 使用实时环境数据恢复您的开发数据库
- 运行
update-database
,它应该运行从你的分支迁移,并抱怨'无法更新数据库匹配当前模型等等等等等等。 - 运行
add-migration MergeBranchBToMaster -ignoreChanges
,这将创build一个空的迁移。 - 再次运行
update-database
- 推送您的更改
步骤3中的魔法基本上告诉EFclosures不匹配的模型,因此非常确定您的迁移不会与现场环境中的迁移冲突。 如果他们这样做,你总是可以创buildSQL脚本来推送缺失的迁移(这实际上是首选的方法)。
原始答复
基于@Ladislav Mrnka的回答,我发现了一个相当直接的解决scheme。 这将与现场环境[1]一起工作,你只需要小心,不要改变任何部署的迁移。
-
在合并之前,请注意您添加的迁移(MyMigration)以及之前的迁移(BaseMigration)
-
在git中合并分支
-
打开软件包pipe理器控制台,然后运行:UPDATE-DATABASE -TargetMigration:BaseMigration。 这将使您的数据库恢复到任何冲突的迁移应用之前的状态
-
删除您的本地迁移(MyMigration)
-
运行:UPDATE-DATABASE。 这将适用于其他分支完成的所有较新的迁移。
-
运行:ADD-MIGRATION MyMigration。 这将根据数据库的当前状态重新生成本地迁移,如git -rebase。
-
运行:UPDATE-DATABASE。 使用本地迁移更新数据库。
如果您有多个本地迁移,这也可以工作,但它会将它们合并成一个。
[1]通过与现场环境合作,我的意思是所产生的移民可以适用于可能已经有一些/所有其他部门的移民适用的生活环境。 这些步骤本身纯粹是为了发展的目的。
Rowan Miller在第9频道: 迁移 – 团队环境中做了一个很棒的video。 它是指entity framework6。
它描述了一个场景,其中第一个开发人员A和B在同一个模型上工作,A在第一个检查。 现在开发者B必须处理他从A获取最新版本时遇到的问题。
这与在不同分支之间发生冲突基本相同,因为一般问题是合并同一时间完成的迁移更改,但实际上具有不同的模型源状态。
解决scheme是:
- 在解决版本控制系统的冲突时,开发者B必须同时接受他自己和开发者A的改变。
- 开发人员B的
UpdateDatabase
命令仍然会失败(错误消息: “无法更新数据库以匹配当前模型,因为有未决的更改…” ) - 开发人员B必须使用
IgnoreChanges
选项创build一个“空迁移”:
Add-Migration NameOfMigration -IgnoreChanges
然后UpdateDatabase
命令将成功。
问题的根源
更新数据库时发生的错误的来源是因为EF在迁移文件内的resx文件中存储了迁移引用的模型的快照。
在这种情况下,在获取/合并由开发者A做出的改变之后,开发者B“当前模型”的快照是不正确的。
我已经思考了一些问题,希望能对这里提出的不同意见和做法做出贡献。
考虑你的本地迁移实际上代表什么。 当在开发数据库本地工作时,我使用迁移来以最方便的方式更新数据库,当添加列等到表,添加新的实体等
所以,Add-Migration会检查我当前的模型(我们称之为模型b)和我以前的模型(模型a),并生成一个从数据库中的a => b的迁移。
对于我来说, 如果每个人确实拥有自己的数据库,然后在组织中存在某种阶段/testing/开发/生产数据库服务器, 那么尝试合并我的迁移和其他任何迁移都是没有意义的。 这一切都取决于团队是如何build立起来的,但如果要真正以分散的方式进行工作,将其他人所做的改变隔离开来是有意义的。
那么,如果你的工作分布,并有一些实体,人,例如,你的工作。 出于某种原因,许多其他人也在努力。 所以,你在sprint中为你的特定故事需要添加和删除Person的属性(我们都在这里工作敏捷,是不是?),就像你最初做成一个整数的社会安全号码,因为你不是那明亮,然后到一个string等
您添加名字和姓氏。
然后你就完成了,你有十个奇怪的上下迁移(你可能在工作时删除了一些工作,因为他们只是废话),你从中央Git仓库中获取一些变化。 哇。 你的同事鲍勃也需要一些名字,也许你应该互相交谈?
无论如何,他已经添加了NameFirst和NameLast,我猜…那么你是做什么的? 那么,你合并,重构,改变,所以它有更多的理智的名字…像名字和姓氏,你运行你的testing,并检查他的代码,然后你推到中央。
但是迁移呢? 那么,现在是时候进行移动中央回购,或更具体地说,分支“testing”包含一个很好的从模型a =>模型b的小迁移。 这个迁移将是唯一一个迁移,而不是十个怪异的迁移。
你看到我在做什么? 我们正在与可爱的小pocos和比较他们构成了实际的迁移。 所以,我们不应该合并迁移,我认为我们应该有每个分支的迁移或类似的东西。
事实上,我们甚至需要在合并之后在分支中创build迁移? 是的,如果这个数据库自动更新,我们需要。
得多工作一下,至less这是我的想法。
考虑使用不会导致这些冲突的其他迁移库,例如FluentMigrator或Migrator.NET。
我不认为EF迁移是真的准备好用于分支和合并的一般用途 – 这是很多工作,而且容易犯下令人讨厌的错误。
我认为@LavaEater所说的话很有意义。 我正在实施分支策略(Development,Main,Release),并将其与开发,质量保证和发布过程中的环境alignment。
- 发展部门 – 地方发展
- 主分支 – 从开发分支合并更改并部署到我的登台环境(一个Azure网站和SQL数据库)
- 发布分支 – 从主合并更改并部署到生产环境(另一个Azure网站和SQL数据库)
我已经提出了上面讨论的问题,我认为迁移的复杂性和潜在的解决方法会给发布过程带来很大的风险。 在Development,Main和Release中执行独立迁移有效地意味着,在Dev中构build的架构不是在Staging中进入QA的架构,而在Staging上QA签署的架构不是部署到Live的架构除非我遵循build议的解决scheme之一,我敢肯定会工作,但可能容易出错)。
回复@LavaEater – 首先从EF代码中获得的真正好处是什么? 就我个人而言,我认为这是我可以从代码生成模式(并可能调整自动生成的迁移,如果我想)轻松。 之后,迁移是一个简单的部署过程的复杂化。
我目前的想法是先使用代码来生成开发中的迁移,然后:
-
选项A) – 使用Update-Database -script编写架构更改并将其置于源代码pipe理之下。 如果有两个人正在修改相同的模型,那么仍然存在一些冲突的可能性,但是我认为pipe理起来更容易。
-
选项B) – 使用类似SQL比较来生成模式更改脚本。 这可能更灵活,更透明,因为我喜欢看到我应用于生产数据库的确切模式更改(称为偏执狂)。
我错过了什么吗? 我想在main和release分支中(假设数据库将被脚本创build和更新)将会有一些configuration来禁用代码优先迁移。 除此之外,这感觉像一个安全的解决scheme,但我会重视第二意见。