I've been working on a .NET project for the past few months. In this project we're using the Entity Framework, which is an ORM for the .NET ecosystem. It's got a really nice interface to it. The migrations are written in code, the entities are expressed as a class with properties, and those properties can be annotated with different attributes.
One problem we've run into is deciding how to manage distributed migrations. We've come across some issues when two separate stories require database schema changes. It turns out that the Entity Framework has a very specific process when creating and executing migrations. I've pored over documentation and blog posts on the subject, and there are a couple of solutions. I'd like to shed a light on what I see as the most responsible way to manage it, in the simplest way possible. We'll build up the scenario, highlight the problem, and elegantly manage the outcome.
Creating the First Migration
It's relatively easy to get started using the code-first migration mindset. Create an entity.
Add a database set for it in the Database context.
Enable the migrations in the package manager console.
Add the migration.
Add-Migration is going to build out a migration.
Now update the database to run the new migration.
The database now reflects the entities that have been created.
Divide and Conquer
Working in teams allows work to be parallelized. Working on completely separate features allows more progress to be made than stepping on another's toes in the same feature.
We've got a developer named Jack. He's working on adding a feature that changes the entity.
He adds a migration.
There's another devleoper named Jill. She's working on another feature that changes the same entity.
She also adds a migration.
Jill happens to finish her feature first, so she merges her story into master. When Jack goes to merge into master he sees that he can't merge cleanly, since there are conflicts in the entity. He rebases master, and fixes all of the merge conflicts. When he runs the tests, he is notified that there are migrations to be run.
No problem, He just updates the database.
He runs the tests but gets the same warning.
When a migration is created, some metadata and a snapshot of the current entity will also be created. This is necessary in order for the framework to be able to build smarter scaffolds as the entities change, but it's also a source of confusion. The confusion occurs when a new migration is pulled in after a local migration has been run.
The repetitive warning gives no hint for how to handle the scenario, and running the application results in an InvalidOperationException.
Though not intuitive, the answer is a quick three-step process:
1) Roll the database back to the latest migration that was merged in.
This puts the database in a state just before the newest migration should take place.
2) Re-add the local migration. It is suggested to use the timestamp-qualified migration name, however the semantic name will typically suffice.
Re-adding the migration at this point won't acutally create a new migration. It will instead update the metadata and model state of the already existing migration to reflect the state we've set the database to.
3) Rerun the migration.
And there you have it. Progress may resume as usual without any complicated git cherry-pick workflows, and no silly empty migrations titled MigrationConflict.