Monday 9 December 2013

Entity Framework Database Migrations are broken for teams

So one of the things that came out of the Entity Framework (EF) model first was migrations, the ability to update your upgrade your database as your model evolved and even better can do this for you automatically.  Sounds like nirvana for developing against RDBMS.

Its just a shame that is fundamentally flawed if you have more than one person in your team making changes to the db at the same time.

The cause of the problem

The reason the migrations are broken is due to how EF Migrations creates a migration (be it automatic or code).

When you create a new migration via the Package Manager EF looks at that database and stores a hash of the schema and then when it comes to include the migration if the hash doesn’t match what it expects it won’t use it, and there’s nothing you can do about it.

What I don’t understand is that like many other migration frameworks EF Migrations have a table in the database to record which migrations have been run

Types of Migration

Before we get into the why the migrations are broken a little background on the different types of migration

Automatic migrations

Automatic migrations create a migration with no input needed from the developer, it looks at your model and previous migrations and works out what’s needed.

Only problem is that even automatic migrations can’t do everything needed for a migrations e.g. column renames and MS themselves recommend you don’t use them in team environments.  If you Google EF Migrations you’ll find other blog posts that tell you not to use automatic migrations at all, they’re great for demo’s but beyond that you’re better off leaving them alone.

Code migrations

Code migrations are where the developer creates the migration themselves and provide the necessary functionality to both apply and reverse the changes to the database.

Doing this gives the developer controller over the changes which can be made via the objects EF Migrations exposes or by being able to issue Sql statements directly at the database allowing for those tricky scenarios which can’t be handled through the objects.

The cause of the problem

The reason the migrations are broken is due to how EF Migrations creates a migration (be it automatic or code).

When you create a new migration via the Package Manager EF creates a hash of the model that the migration was created against, this hash is what causes the problem since the model of each developer creating a migration won’t match after they have applied the migration and whoever merges last will lose out.

Try it for yourself

You can try this out for yourself, yes the example is a little contrived but its the easiest way to deliberately cause the problem. To make it eaier to reproduce I have prepared a repository that has an initial migration to create the database which has a couple of code branches which you use to create the migrations.

  1. Clone repository
  2. on master branch in Package manager console run the following command: 
    Update-Database -TargetMigration:'AddPostClass'
  3. Switch to BreakMigration branch
  4. In the Package manager console run following command: Add-migration ‘AddBlogAbstract’
  5. Save all and then commit the changes
  6. Swap to master branch
  7. In the Package manager console run following command: Add-migration ‘AddPublishedDate’
  8. Save all and then commit the changes
  9. Merge BreakMigration branch into master
  10. In Package manager console run: Update-Database

You’ll then be presented with a message :

Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration.
You can use the Add-Migration command to write the pending model changes to a code-based migration.

At this point if you are the person trying to apply the migration your heart falls and you set about trying to rectify the situation.

When you hit this the easiest way I’ve found to sort the problem is to delete the migration you are trying to apply, run the Update-database command and then re-run Add-Migration <migration name> –IgnoreChanges, hopefully you only created 1 migration before this happened.

Should you use EF Migrations?

EF Migrations are a full featured migration framework and have a lot of good things going for it, especially if you are using Code-First, but if I was working in a team environment with multiple developers making changes to the database I would look at alternatives to avoid the issue altogether.

Further reading

There are various blogs I found about this but the best is a very in-depth article on MSDN Data Developer Center which explains how the migrations work at a low level, why they use the hash and a couple of options to resolve the issue if you run into it.