在协作环境中迁移entity framework

我们有多个开发人员在使用Entity Framework 5.0的项目上工作。 每个开发人员都使用他自己的本地SQL 2012数据库,以便在不妨碍其他人的情况下开发和testing。

起初,我们使用了自动迁移和基于代码的迁移的混合。 这根本不能很好地工作,所以我们决定禁用自动迁移,只允许基于代码。 我应该补充一点,我们再次启动一个干净的数据库,没有从所有的自动迁移中“损坏” _MigrationsHistory

所以现在的工作stream程是:

  1. 开发人员更改他的数据模型
  2. add-migration <Name>并将其应用到他的数据库与update-database
  3. 检查数据模型更改和迁移到Git。
  4. 另一个开发人员将接收更改并将其应用于其数据库。

到目前为止,这工作得很好。 然而,在今天之前,通常只有我是使移民和其他人实行移民。 但是今天有三个开发者的迁移。 我只是把这些迁移,做了一个update-database ,这很好。

然而,我也对自己的datamodel进行了修改,所以在update-database的最后,它给了我一个警告,说明我还没有及时更新,所以我做了add-migration <my migration> 。 但是,当它支持迁移时,它给了我已经应用到数据库的所有迁移的变化。 所以:它试图删除已经被删除的列,尝试创build一个已经存在的表等。

怎么可能? 我的假设是,EF只会检查_MigrationsHistory表,并找出表中尚未出现哪些迁移,然后逐一应用名称中包含的时间戳。 但显然不是,因为即使当我撤消自己的变化,我有一个干净的环境,它仍然抱怨我的数据库不与模型同步。 但我只是把这些改变,并将其应用到我的数据库。 它同步的。 我可以看到我刚刚在_MigrationsHistory表中应用的迁移。

我能想到的唯一的事情是我添加了一个属性到一个datamodel,不会导致数据库的变化(我添加了一个List<X> datamodel Y其中X是在一对多关系中的许多。这不会导致数据库更改,因为X已经有一个外键到Y)。 那可以吗? 如果是这样,这真的很脆弱,因为没有办法为此添加迁移,因为没有数据库更改,我不知道如何解决这个问题。

我不知道如何处理这个问题,因为我当然可以编辑它的脚手架,并删除已经应用到我的数据库的所有东西。 但是呢? 我检查了一下,然后其他一些开发者得到了同样的信息,即使在应用我的新变更之后,他的数据库也没有及时更新,脚手架自己的变化,得到同样的废话脚手架,编辑它,检查它,然后下一个开发人员得到它。 它变成了一个恶性循环,与我们使用自动迁移时所遇到的类似,我认为我们已经通过切换到仅基于代码的方式来解决这个问题。 现在我不能相信它做正确的事情,这是一个噩梦,这样的工作。

我也尝试过使用update-database -t:201211091112102_<migrationname>将我从同事那里获得的迁移一一添加,但无济于事。 它仍然给我错误的脚手架。

那么,我们在这里做错了什么,或者EF是不是为这样的协作而build?

UPDATE

我创build了一个可重复的testing用例,但是为了模拟这个多用户/多数据库场景,这是一个漫长的舞蹈。

https://github.com/JulianR/EfMigrationsTest/

有上述项目时重现的步骤(这些步骤也存在于代码中):

  1. 添加迁移Init
  2. 更新数据库(在数据库'TestDb')
  3. 将连接string更改为指向TestDb1
  4. TestDb1上的更新数据库
  5. 在类Test上取消对属性Foo的注释
  6. 添加迁移M1将属性Foo添加到TestDb1
  7. 再次注释掉Test.Foo
  8. 将连接string更改为指向TestDb2
  9. 从项目中排除迁移M1,所以它不适用于TestDb2
  10. 在类Test上取消注释属性栏
  11. update-database将Init迁移应用到TestDb2
  12. 添加迁移M2以将属性Bar添加到TestDb2
  13. 更改连接string以再次指向原始TestDb
  14. 再次将迁移M1包含到项目中
  15. 在类Test上取消对属性Foo的注释
  16. 在类Test上取消注释SomeInt属性
  17. 更新数据库
  18. 添加迁移M3
  19. update-database,得到一个错误,因为M3试图将Foo列添加到数据库TestDb中,这个数据库刚刚被迁移M1添加。

以上是模拟三个用户,其中用户1在他的数据库中,另外两个用户使用他的初始化来创build他们的数据库。 然后,用户2和用户3都对数据模型进行自己的更改,并将其添加到源控件以及应用更改所需的迁移。 然后用户1拉取用户2和3的变化,而用户1也自己对数据库进行了更改。 然后,用户1调用update-database来应用用户2和用户3的更改。然后,他自己的迁移脚本,然后错误地将用户2或3的更改添加到脚手架迁移,这会在应用到用户1的数据库时导致错误。

您需要像编写冲突一样手动解决迁移冲突。 如果更新并且有新的迁移,则需要确保上次迁移后的元数据与当前模型匹配。 要更新迁移的元数据,请为其重新发出“添加迁移”命令。

例如,在您的scheme中的步骤17(更新数据库)之前,应该发出以下命令

 Add-Migration M2 

这将更新元数据以使其与当前模型同步。 现在,当您尝试添加M3时,它应该是空白的,因为您没有进行任何进一步的模型更改。

您需要添加一个空白的“合并”迁移,将重置.resx文件中最新迁移的快照。 使用IgnoreChanges开关执行此操作:

Add-Migration <migration name> -IgnoreChanges

看到这里的解释

在我们的环境中,我们遇到了类似的问题,以下是我们迄今为止所了解的情况以及我们如何解决这个问题:

当您应用更改(update-database)但未检入时,然后从其他没有进行更改的开发人员那里接收更改时,这似乎是事情不同步的地方。 根据我们的经验,当您执行更新数据库过程时,似乎为您自己的更改保存的元数据会被其他开发者的元数据覆盖。 其他开发人员没有更改,所以保存的元数据不再是数据库的真实反映。 当EF做了比较之后,由于元数据的变化,它认为你的变化实际上是新的。

一个简单的,丑陋的解决方法是做另一个迁移,并摧毁它的内容,所以你有空()和空的down()方法。 应用该迁移并将其检入到源代码pipe理中,并让每个人都同步到该源代码。 这简单地同步所有的元数据,所以每个人都有所有的变化。

我在codeplex上添加了一个问题,这个问题也导致了我们团队中的许多人头疼。

链接是https://entityframework.codeplex.com/workitem/1670

我已经思考了一些问题,希望能对这里提出的不同意见和做法做出贡献。

考虑你的本地迁移实际上代表什么。 当在开发数据库本地工作时,我使用迁移来以最方便的方式更新数据库,当添加列等到表,添加新的实体等

所以,Add-Migration会检查我当前的模型(我们称之为模型b)和我以前的模型(模型a),并生成一个从数据库中的a => b的迁移。

对于我来说,如果每个人确实拥有自己的数据库,然后在组织中存在某种阶段/testing/开发/生产数据库服务器,那么尝试合并我的迁移和其他任何迁移都是没有意义的。 这一切都取决于团队是如何build立起来的,但如果要真正以分散的方式进行工作,将其他人所做的改变隔离开来是有意义的。

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

您添加名字和姓氏。

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

无论如何,他已经添加了NameFirst和NameLast,我猜…那么你是做什么的? 那么,你合并,重构,改变,所以它有更多的理智的名字…像名字和姓氏,你运行你的testing,并检查他的代码,然后你推到中央。

但是迁移呢? 那么,现在是时候进行移动中央回购,或更具体地说,分支“testing”包含一个很好的从模型a =>模型b的小迁移。 这个迁移将是唯一一个迁移,而不是十个怪异的迁移。

你看到我在做什么? 我们正在与可爱的小pocos和比较他们构成了实际的迁移。 所以,我们不应该合并迁移,我认为我们应该有每个分支的迁移或类似的东西。

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

另一个需要考虑的事情是,在从中央仓库进行拉动之前,从不实际创build一个移动。 这意味着在创build迁移之前,您将同时获得其他团队成员的迁移代码及其对模型的更改。

得多工作一下,至less这是我的想法。

选项1:添加空白的“合并”迁移

  1. 确保本地代码库中的任何未决模型更改已写入迁移。 此步骤可确保您在生成空白迁移时不会错过任何合法更改。
  2. 与源代码控制同步。
  3. 运行Update-Database以应用其他开发人员已经签入的任何新的迁移。**注意:****如果您没有从Update-Database命令中收到任何警告,那么没有来自其他开发人员的新迁移,不需要进一步的合并。
  4. 运行Add-Migration -IgnoreChanges(例如Add-Migration Merge -IgnoreChanges)。 这将生成包含所有元数据(包括当前模型的快照)的迁移,但会将当前模型与上次迁移中的快照(意思是获取空白的Up和Down方法)进行比较时忽略所检测到的任何更改。
  5. 继续开发,或提交源代码pipe理(当然运行你的unit testing)。

选项2:更新上次迁移中的模型快照

  1. 确保本地代码库中的任何未决模型更改已写入迁移。 此步骤可确保您在生成空白迁移时不会错过任何合法更改。
  2. 与源代码控制同步。
  3. 运行Update-Database以应用其他开发人员已经签入的任何新的迁移。**注意:****如果您没有从Update-Database命令中收到任何警告,那么没有来自其他开发人员的新迁移,不需要进一步的合并。
  4. 运行Update-Database -TargetMigration(在这个例子中,我们一直在关注这个是Update-Database -TargetMigration AddRating)。 这将数据库angular色转换回第二个迁移的状态 – 从数据库有效地“取消”最后一次迁移。 **注意:****由于元数据也存储在数据库的__MigrationsHistoryTable中,因此需要执行此步骤才能安全地编辑迁移的元数据。 这就是为什么如果最后一次迁移仅在您的本地代码库中,您应该只使用此选项。 如果其他数据库应用了上一次迁移,则还需要将其回滚并重新应用上次迁移以更新元数据。
  5. 运行Add-Migration(在这个例子中,我们一直在追踪这个,就像Add-Migration 201311062215252_AddReaders)。 **注意:****您需要包含时间戳,以便迁移知道您要编辑现有的迁移,而不是搭build一个新的迁移。 这将更新上次迁移的元数据以匹配当前模型。 命令完成后,您会收到以下警告,但这正是您想要的。 “只有迁移devise器代码”201311062215252_AddReaders“被重新脚手架。 要重新构build整个迁移,请使用-Force参数。“
  6. 运行更新数据库以使用更新的元数据重新应用最新的迁移。
  7. 继续开发,或提交源代码pipe理(当然运行你的unit testing)。

MSDN有一个伟大的文章。 请通过它。

团队环境中的entity framework代码优先迁移

我能够想出的解决scheme(至less2个用户,没有testing3)是:

  1. 合并迁移以同步元数据运行更新数据库(这应该失败),然后
  2. 添加数据库然后
  3. 删除up()down()方法中的所有生成的代码

这仍然会由更新数据库运行,但不会做任何事情,只是使元数据同步。

我同意@LavaEater。 这个问题的核心,似乎是移民脚手架应该集中。 也许作为一个自动/集成构build过程的一部分,每次推动发生? 此后,可以由团队成员从服务器中提取所产生的迁移。

这意味着他们自己的迁移脚本不应该推送到服务器。