C# 如何在具有多个分支的项目中管理迁移?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10556348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to manage Migrations in a project with multiple branches?
提问by Christofer Eliasson
I have an ASP.NET MVC3 project that uses Entity Framework 4.3 with the code-first approach. I use Migrations to keep the database up-to-date.
我有一个 ASP.NET MVC3 项目,它使用 Entity Framework 4.3 和代码优先方法。我使用迁移来使数据库保持最新。
The project is under source-control and I have a number of branches. What I just realized is that there will be a problem when I want to merge one of my branches into the master. Since I have created migration-files in both branches, there will be overlapping migrations when I merge, which will probably cause conflicts.
该项目受源代码控制,我有许多分支。我刚刚意识到,当我想将我的一个分支合并到 master 时会出现问题。由于我在两个分支都创建了migration-files,合并的时候会出现重叠的migration,可能会造成冲突。
Is there a good way to manage Migrations in a project with multiple branches?
在具有多个分支的项目中管理迁移有什么好方法吗?
Update
更新
One way would be to merge, then delete all migration-files created while the branches were separate, and then create one new migration file that holds all changes from the time the branch was created until it was merged back in. This would work for the dev-environment where you can dump the database and re-build it with all the migration-files. The problem then would be the live-environment. Since you couldn't roll back to the time the branch was created without the risk of loosing data, there will be a conflict when you try to use your new migration-file to update the live database.
一种方法是合并,然后删除在分支分开时创建的所有迁移文件,然后创建一个新的迁移文件,该文件包含从创建分支到重新合并分支的所有更改。这适用于dev-environment 您可以在其中转储数据库并使用所有迁移文件重新构建它。那么问题将是生活环境。由于您无法在没有丢失数据风险的情况下回滚到创建分支的时间,因此当您尝试使用新的迁移文件更新实时数据库时会发生冲突。
采纳答案by oldwizard
There is a much better solution for handling entity framework migration merge conflictson a similar question.
有一个更好的解决方案来处理类似问题上的实体框架迁移合并冲突。
All you need to do after a merge is to re-scaffold the meta data of the migration in the target branch. That is you do not rescaffold the up/down code, just the state in the resx-file.
合并后您需要做的就是在目标分支中重新构建迁移的元数据。也就是说,您不重新构建向上/向下代码,而只是重新构建 resx 文件中的状态。
add-migration [the_migration_to_rescaffold_metadata_for]
This almost always works. The procedure will fail if a different migration in the merge have changed the database in such a way that the migration is no longer runnable or gives an unexpected result. That being said - I believe that to be a very rare case as most migrations should be auto-generated, or at least not be dependent on other tables that are not changed in the migration itself as well.
这几乎总是有效。如果合并中的不同迁移改变了数据库,导致迁移不再可运行或产生意外结果,则该过程将失败。话虽这么说 - 我相信这是一种非常罕见的情况,因为大多数迁移应该是自动生成的,或者至少不依赖于迁移本身未更改的其他表。
One such case where rescaffold state would fail could be:
脚手架状态失败的一种情况可能是:
Column foo is an int and rows contain [0, 1, 2]
Migration A from branch A change foo to boolean (0 will become false automatically and > 0 will become true)
Migration B from branch B change foo to string. It expects it to be an int but it is a boolean, the migration will succeed though. Data will be lost since when migration B was created the rows would contain ["0", "1", "2"]. When migration A altered column to boolean (and did so successfully and with expected result) the rows will now contain ["0", "1", "1"] instead and Migration B will have a different end result than what was observed in Branch B.
列 foo 是一个整数,行包含 [0, 1, 2]
从分支 A 迁移 A 将 foo 更改为布尔值(0 将自动变为假,> 0 将变为真)
从分支 B 迁移 B 将 foo 更改为字符串。它期望它是一个 int 但它是一个布尔值,但迁移会成功。数据将丢失,因为在创建迁移 B 时,行将包含 ["0", "1", "2"]。当迁移 A 将列更改为布尔值(并且成功并具有预期结果)时,这些行现在将包含 ["0", "1", "1"] 并且迁移 B 将具有与观察到的不同的最终结果分行 B。
There are probably more edge cases where things could go wrong with the solution. But if migrations up/down code is not dependent on things changed by another migration in the merge it should work well to just update the metadata in the migrations.
可能还有更多边缘情况,解决方案可能会出错。但是,如果向上/向下迁移代码不依赖于合并中另一个迁移所更改的内容,则只需更新迁移中的元数据就可以很好地工作。
回答by Ladislav Mrnka
Merging migrations is IMHO manual task. Part of migration code is auto-generated and we usually don't merge auto-generated code - instead we run autogeneration again after the merge.
合并迁移是恕我直言的手动任务。部分迁移代码是自动生成的,我们通常不会合并自动生成的代码——而是在合并后再次运行自动生成。
Until ADO.NET team provides some recommendation I would follow simple principle:
在 ADO.NET 团队提供一些建议之前,我会遵循简单的原则:
- Before you do the merge revert the master database to the version used prior to branching
- Merge your branches
- Exclude migration classes created after branching from merged assembly
- Add a new migration for merged code base which will migrate your database in the state prior to branching to the state after merging branches
- If your excluded migration classes contain some customization merge them to the new migration class
- Run migration to migrate your database to current merged version
- 在进行合并之前,将主数据库恢复到分支之前使用的版本
- 合并你的分支
- 排除从合并的程序集分支后创建的迁移类
- 为合并的代码库添加一个新的迁移,它将在分支之前的状态下迁移您的数据库到合并分支后的状态
- 如果您排除的迁移类包含一些自定义将它们合并到新的迁移类
- 运行迁移将您的数据库迁移到当前合并版本
If your branches contained multiple migration steps (version) you will lose them and you will end with two versions - prior to branching and after merging.
如果您的分支包含多个迁移步骤(版本),您将丢失它们并且您将以两个版本结束 - 分支之前和合并之后。
Edit:
编辑:
It will not work in live environment. The problem here would be the development process itself. If you have live environment you should keep its branch untouched (except minor bug fixes). If you continue development in that branch with production deployment and in the same time you build another version in separate branch without continuous integration (= continuous merging changes back to the main branch to integrate your new development with the main code base) you have a big problem. I think migrations in general cannot handle this.
它不会在现场环境中工作。这里的问题是开发过程本身。如果您有实时环境,则应保持其分支不变(小错误修复除外)。如果您通过生产部署继续在该分支中进行开发,同时在没有持续集成的情况下在单独的分支中构建另一个版本(= 不断将更改合并回主分支以将您的新开发与主代码库集成)问题。我认为迁移一般无法处理这个问题。
The only option in such case would probably be removing all migrations from merged solution and deleting MigrationHistorytable from the database. Than you can enable migrations on the project again and add initial migration to use your current database as starting point = no way back to previous version because no information about previous migrations will exist.
在这种情况下,唯一的选择可能是从合并的解决方案中删除所有迁移并MigrationHistory从数据库中删除表。然后您可以再次在项目上启用迁移并添加初始迁移以使用您当前的数据库作为起点=无法返回到以前的版本,因为将不存在有关以前迁移的信息。
回答by Eamon Nerbonne
Consider using a different migration library that doesn't cause these conflicts, such as FluentMigrator or Migrator.NET.
考虑使用不会导致这些冲突的其他迁移库,例如 FluentMigrator 或 Migrator.NET。
I don't think EF migrations are really ready for general use with branches & merges - it's a lot of work, and too easy to make nasty mistakes.
我认为 EF 迁移并没有真正准备好与分支和合并一起使用——它需要做很多工作,而且很容易犯下严重的错误。
回答by Bill Yang
Edit:a colleague of mine discovered an easier with to do this, I left my original answer at the bottom for completeness.
编辑:我的一位同事发现这样做更容易,为了完整起见,我将原始答案留在了底部。
(VERY IMPORTANT) migrations in live environment must not conflict with ones in your current branch, otherwise you need to redo all your migrations and resolve data model change conflicts by hand.
(非常重要)实时环境中的迁移不能与您当前分支中的迁移发生冲突,否则您需要重做所有迁移并手动解决数据模型更改冲突。
- restore your development database with live environment data
- run
update-database, it should run migrations from your branch, and complain about 'unable to update database to match the current model blah blah..' - run
add-migration MergeBranchBToMaster -ignoreChanges, this will create an empty migration. - run
update-databaseagain - push your changes
- 使用实时环境数据恢复您的开发数据库
- 运行
update-database,它应该从您的分支运行迁移,并抱怨“无法更新数据库以匹配当前模型等等……” - run
add-migration MergeBranchBToMaster -ignoreChanges,这将创建一个空迁移。 update-database再次运行- 推动你的改变
The magic in step 3 basically tells EF to shutup about mismatched models, hence be very sure that your migrations do not conflict with ones in live environment. If they do, you can always create SQL scripts for pushing the missing migrations (which is actually the preferred method).
步骤 3 中的魔法基本上告诉 EF 关闭不匹配的模型,因此请确保您的迁移不会与实时环境中的迁移发生冲突。如果他们这样做,您始终可以创建 SQL 脚本来推送丢失的迁移(这实际上是首选方法)。
Original Answer
原答案
I have found a fairly straight-forward solution based on @Ladislav Mrnka's answer. This will work with live environment[1], you just have to be careful not to change any deployed migrations.
我根据@Ladislav Mrnka 的回答找到了一个相当直接的解决方案。这将适用于实时环境[1],您只需要注意不要更改任何已部署的迁移。
Before Merge, take note of the migration you added (MyMigration), and its previous migration (BaseMigration)
Merge branches in git
Open Package Manager Console, and run: UPDATE-DATABASE -TargetMigration:BaseMigration. This will revert your database to the state before any of the conflicted migrations are applied
Delete your local migration (MyMigration)
Run: UPDATE-DATABASE. This will apply all newer migrations done in other branches.
Run: ADD-MIGRATION MyMigration. This will re-generate your local migration based on current state of database, like git -rebase.
Run: UPDATE-DATABASE. Update database with you local migration.
在 Merge 之前,记下您添加的迁移 (MyMigration) 及其之前的迁移 (BaseMigration)
在git中合并分支
打开包管理器控制台,然后运行:UPDATE-DATABASE -TargetMigration:BaseMigration。这会将您的数据库恢复到应用任何冲突迁移之前的状态
删除您的本地迁移 (MyMigration)
运行:更新数据库。这将应用在其他分支中完成的所有较新的迁移。
运行:ADD-MIGRATION MyMigration。这将根据数据库的当前状态重新生成您的本地迁移,例如 git -rebase。
运行:更新数据库。使用本地迁移更新数据库。
This also works if you have multiple local migrations, but it will merge them all into a single one.
如果您有多个本地迁移,这也适用,但它会将它们全部合并为一个。
[1] by working with live environment, I mean that the generated migration can be applied to live environment which may already have some/all of the other branches' migrations applied. The steps themselves are purely for development purpose.
[1] 通过使用实时环境,我的意思是生成的迁移可以应用于可能已经应用了一些/所有其他分支迁移的实时环境。这些步骤本身纯粹是为了开发目的。
回答by LavaEater
I have put some thought into this and I hope I will contribute to the different opinions and practices presented here.
我对此进行了一些思考,我希望我能对这里提出的不同意见和做法做出贡献。
Consider what your local migrations actually represent. When working locally with a dev database, I use migrations to update the database in the most convenient way possible when adding columns etc to tables, adding new entities etc.
考虑一下您的本地迁移实际代表什么。在本地使用开发数据库时,在向表中添加列等、添加新实体等时,我使用迁移以最方便的方式更新数据库。
So, Add-Migration checks mycurrent model (let's call it model b) against myprevious model (model a) and generates a migration to go from a => b in the database.
因此,Add-Migration根据我之前的模型(模型 a)检查我当前的模型(我们称之为模型 b),并生成从数据库中的 a => b 开始的迁移。
To me it makes very little sense to try and merge my migrationswith anyone elses migrations, ifeveryone indeed has their own database and there then exists some kind of stage / test / dev / production database servers in the organization. This all depends on how the team has it set up, but it makes sense to insulate each other from changes that other people make if you want to truly work in a distributed manner.
对我来说,尝试将我的迁移与其他任何人的迁移合并是没有意义的,如果每个人确实有自己的数据库,然后在组织中存在某种阶段/测试/开发/生产数据库服务器。这一切都取决于团队的设置方式,但如果您想真正以分布式方式工作,那么将彼此与其他人所做的更改隔离开来是有意义的。
Well, if you work distributed and have some entity, Person, for example, that you work on. For some reason, lots of other people are also working on it. So, you add and remove properties on Person as needed for your particular story in the sprint (we're all working agile here, aren't we?), like Social Security number that you first made into an integer because you aren't that bright and then to a string etc.
好吧,如果您分布式工作并且有一些实体,例如,您正在处理的人员。出于某种原因,很多其他人也在研究它。因此,您可以根据需要为冲刺中的特定故事添加和删除 Person 上的属性(我们都在这里敏捷工作,不是吗?)那个明亮,然后到一个字符串等。
You add FirstName And LastName.
您添加名字和姓氏。
You are then done and you have ten weird up and down migrations (you probably removed some of them while working since they were just crap) and you fetch some changes from the central Git repo. Wow. Your colleague Bob also needed some names, maybe you should've talked to each other?
然后你就完成了,你有十次奇怪的上下迁移(你可能在工作时删除了其中的一些,因为它们只是废话)并且你从中央 Git 存储库中获取一些更改。哇。你的同事鲍勃也需要一些名字,也许你们应该互相谈谈?
Anyways, he has added NameFirst and NameLast, I guess... so what do you do? Well, you merge, refactor, change so it has more sane names... like FirstName and LastName, you run your tests and check his code, and then you push to the central.
不管怎样,他已经添加了NameFirst 和NameLast,我猜……那你怎么办?好吧,您合并、重构、更改以使其具有更合理的名称……例如 FirstName 和 LastName,您运行测试并检查他的代码,然后推送到中央。
But what about the migrations? Well, now would be the time to make a migration moving the central repo, or the branch "test" more specifically, contain a nice little migration from itsmodel a => model b. This migration will be one and only one migration, not ten weird ones.
但是迁移呢?好吧,现在是时候进行迁移,移动中央存储库,或者更具体地说,分支“测试”包含从其模型 a => 模型 b的漂亮小迁移。这次迁移将是一次且只有一次迁移,而不是十次奇怪的迁移。
Do you see what I'm getting at? We are working with nice little pocos and the comparisons of them constitute the actual migrations. So, we shouldn't merge migrations at all, in my opinion, we should have migrations-per-branch or something like that.
你明白我在说什么吗?我们正在使用漂亮的小 pocos,它们的比较构成了实际的迁移。所以,我们根本不应该合并迁移,在我看来,我们应该有每个分支的迁移或类似的东西。
In fact, do we even need to create the migration in the branch after merge? Yes, if this database is updated automatically, we need to.
事实上,我们甚至需要在合并后在分支中创建迁移吗?是的,如果这个数据库是自动更新的,我们需要。
Gotta work some more, those are my thoughts on this, at least.
还得再努力一些,至少这是我对此的看法。
回答by Liam Weston
I think what @LavaEater is saying makes a lot of sense. I'm implementing a branching strategy (Development, Main, Release) and aligning it with the environments in the development, QA and release process.
我认为@LavaEater 所说的很有道理。我正在实施分支策略(开发、主要、发布)并将其与开发、质量保证和发布过程中的环境保持一致。
- Development branch - Local development
- Main branch - Merge changes from Development branch and deploy to my Staging environment (an Azure website and SQL database)
- Release branch - Merge changes from Main and deploy to Production environment (another Azure website and SQL database)
- 开发分支 - 本地开发
- 主分支 - 合并来自开发分支的更改并部署到我的临时环境(Azure 网站和 SQL 数据库)
- 发布分支 - 从 Main 合并更改并部署到生产环境(另一个 Azure 网站和 SQL 数据库)
I've come up against the problem discussed above and in my opinion the complications around migrations and the potential workarounds introduce a great deal of risk into the release process. Executing independent migrations in Development, Main and Release effectively means that the schema I included in the build in Dev is not the schema that goes into QA on Staging and the schema that QA signs off on Staging is not the schema that is deployed to Live (unless I follow one of the suggested solutions which I'm sure would work but may be error prone).
我遇到了上面讨论的问题,在我看来,迁移的复杂性和潜在的解决方法给发布过程带来了很大的风险。在 Development、Main 和 Release 中有效地执行独立迁移意味着我在 Dev 中构建的架构不是在 Staging 中进入 QA 的架构,并且 QA 在 Staging 签核的架构不是部署到 Live 的架构(除非我遵循我确信可行但可能容易出错的建议解决方案之一)。
To echo @LavaEater - what is the real benefit I get from EF code first? Personally, I think it's the ease with which I can generate a schema from code (and potentially tweak the automatically generated migrations if I want to). After that, migrations are a complication of what should be a simple deployment process.
回应@LavaEater - 我首先从 EF 代码中获得的真正好处是什么?就我个人而言,我认为我可以轻松地从代码生成模式(如果我愿意,还可以调整自动生成的迁移)。在那之后,迁移是一个简单的部署过程的复杂化。
My current thinking is to use code first to generate the migrations in development and then either:-
我目前的想法是首先使用代码来生成开发中的迁移,然后是:-
Option A) - Use Update-Database -script to script up the schema changes and put them under source control. There is still some potential for conflicts if 2 people are amending the same model but I think that it's easier to manage.
Option B) - Use something like SQL Compare to generate schema change scripts. This is potentially more flexible and transparent as I like to see exactly what schema changes I'm applying to my Production database (call me paranoid).
选项 A) - 使用 Update-Database -script 编写架构更改的脚本并将它们置于源代码控制之下。如果 2 个人修改同一个模型,仍然有一些潜在的冲突,但我认为它更容易管理。
选项 B) - 使用类似 SQL 比较的东西来生成架构更改脚本。这可能更灵活和透明,因为我喜欢确切地看到我正在应用到我的生产数据库的模式更改(称我为偏执狂)。
Am I missing something? I imagine there will be some configuration to do to disable code first migrations in the Main and Release branches (on the assumption that the DB will be created and updated by scripts). Other than that it feels like a safe solution but I would value a 2nd opinion.
我错过了什么吗?我想会有一些配置来禁用 Main 和 Release 分支中的代码优先迁移(假设数据库将由脚本创建和更新)。除此之外,它感觉像是一个安全的解决方案,但我会重视第二意见。
回答by Martin
Rowan Miller has made a great video about this topic on channel 9: Migrations - Team Environments. It refers to entity framework 6.
Rowan Miller 在第 9 频道上制作了关于此主题的精彩视频:迁移 - 团队环境。它指的是实体框架 6。
It describes a scenario where first developer A and B are working on the same model and A checks in first. Now developer B has to deal with the problems he has when he gets the latest version from A.
它描述了一个场景,其中第一个开发人员 A 和 B 正在处理相同的模型,并且 A 首先签入。现在开发人员 B 必须处理他从 A 获取最新版本时遇到的问题。
This is essentially the same like having conflicts between different branches, because the general problem is merging migration changes done it the same time but effectively having a different source state of the model.
这本质上与在不同分支之间发生冲突是一样的,因为一般问题是合并同时完成的迁移更改,但实际上具有不同的模型源状态。
The solution is:
解决办法是:
- When resolving the conflicts of the version control system, developer B has to accept both changes from himself and developer A.
- An
UpdateDatabasecommand of developer B would still fail at this time (Error message: "Unable to update database to match the current model because there are pending changes...") - Developer B has to create an "empty migration" using the
IgnoreChangesoption:
- 在解决版本控制系统的冲突时,开发人员 B 必须接受自己和开发人员 A 的更改。
UpdateDatabase开发人员 B的命令此时仍然会失败(错误消息:“无法更新数据库以匹配当前模型,因为存在未决更改...”)- 开发人员 B 必须使用以下
IgnoreChanges选项创建“空迁移” :
Add-Migration NameOfMigration -IgnoreChanges
Add-Migration NameOfMigration -IgnoreChanges
Then the UpdateDatabasecommand will succeed.
然后UpdateDatabase命令就会成功。
Source of the problem
问题的根源
The source of the error occurring when updating the database is because EF stores a snapshot of the model a migration refers to in the resx file within the migration file.
更新数据库时发生错误的根源是因为 EF 在迁移文件中的 resx 文件中存储了迁移引用的模型的快照。
In this case developers B snapshot of the "current model" is not correct after getting / merging the changes made by developer A.
在这种情况下,在获取/合并开发人员 A 所做的更改后,“当前模型”的开发人员 B 快照不正确。

