Code First Approach – Migration issues

In this article, we’ll learn how to solve migration merging issues in the code first approach.

If you are new to the code first approach, you can refer to this article which covers CRUD operations with a code first approach in an MVC application.

Normally, sometimes, code-first approaches create some migration issues, such as when getting the latest from somewhere else or adding new migrations. Basically, this kind of issue occurs for the Entity Framework. It doesn’t know that this migration is updated in the code but not in the database yet. A basic problem we often face is that if you changed a class which is not updated in the database, we can fire the below command to update the database.

Add-Migration YourNewMigrationName

Or, if we have already updated in the database but it is still asking for an update, we can fire the below command to update the database.

Add-migration ResolveConflict -ignoreChanges
The above commands will tell the Entity Framework that the code and the database are the same.

Here, we will solve other issues that are hard to solve and difficult to manage. For example, let’s assume we have one project and multiple developers are working on it and that project has multiple branches like branch A and branch B and some developers are working on A and some on B branch. So they created migration in both branches, pulling the latest commits and merging both branches, and again start working on it. But wait this is not an issue: The main issue will come when we move both branches to production because both branches have migration scripts and A branch will move to production first then B branch. In that case, the B branch will be skipped as a date-time stamp for these scripts will be older than the latest  A branch migration script.

Let’s assume this scenario.

A branch had migrations as:

  1. migration_1
  2. migration_5
  3. migration_12

Assume migration_1,migration_12 etc as a timestamp. The _MigrationHistory table is showing migration_12 as the latest run migration script so next time, it will expect something greater than migration_12 to continue running. We created a new branch as A-Copy1 branch and started working on it, and need a migration script. We created a new migration and it gave migration_13 as new migration. We updated our local database by “update-database” command and _MigrationHistory table shows migration_13 as the latest script and expects something greater than this to run next time.

Also, some developer1 created a new branch from A Branch as A-Copy2 and started working on it, but since developer1 created a branch from A branch, he also has those 3 migrations scripts only:

  1. migration_1
  2. migration_5
  3. migration_12

(Remember migration_13 is only created on our local so it is not merged to A branch yet, so developer1’s branch doesn’t have it.)

So developer1 also needed a migration script and added a new migration script. Based on the time stamp, the system generated him as migration_15 as a migration. And he runs “update-database” command.

So we created migration_13 and he created migration_15 so far, correct? And our _MigrationHistory table shows migration_13 is the latest run script, and on a developer1 machine, the _MigrationHistory table shows migration_15 as the latest run script. Then one hour later, we needed another migration and added one more migration script and it generated as migration_16 and run update-database command. So our _MigrationHistory table shows migration_16 as the latest script.

So meanwhile developer1 got done with his branch A-Copy2 and merged it with A branch without having an issue because:

A branch had:

  1. migration_1
  2. migration_5
  3. migration_12

And the migration he created, migration_15, came and sat at the below. So now he runs the “update database” command and his local _MigrationHistory table already show migration_15 as the latest run script so there is nothing to on his end. We also finished with our branch and now merged with A branch.

We have a problem here. Why?

Because we added migration migration_13 and migration_16 and our _MigrationHistory table shows migration_16 as the latest run script which means when we run update-database command, it won’t execute any migration script less than migration_16!!!

But the moment we merge with A branch, our migration scripts will look like this:

  1. migration_1
  2. migration_5
  3. migration_12
  4. migration_13 (we added)
  5. migration_15 (came with developer1’s changes)
  6. migration_16

What is the issue here?

If we notice, on our local database, migration_15 (the one developer1 added) will NEVER run. Because our _MigrationHistory table tells that the latest run script is at migration_16 so it will look for migration_17 or more to run. But will never run migration_15 and at this point, our DB will be out of sync and we couldn’t get the migration_15 running on our local (imagine developer1 was dropping some table, maybe in migration_15, and we couldn’t run that script so that table would still exist on our local).

How to avoid it?

It is actually pretty easy. Think of it as some sort of logic question. So we have a sequence as:

  1. migration_1
  2. migration_5
  3. migration_12
  4. migration_15on A branch before we merge with it. And we have a sequence like migration_13 and migration_16.

So we need to put migration_13 before migration_15 and put migration_16 after migration_15 to make the sequence ordered — but how? It won’t be easy. But we can do a trick here. What we can do is, before merging, we go to A branch and see which migrations scripts are coming and could break the sequence. The moment we saw migration_15, we can understand it will fall between our migrations migration_13 and migration_16. So to avoid this situation, we can first target to latest common migration with A branch. Basically, we latest common migration script is migration_12, right? After migration_12, A branch has migration_15 and we have migration_13 and migration_16.

So first we can go back to the latest common point by using below command,

update-database -target migration_12
This will roll back our DB changes and update our _MigrationHistory table to show migration_12 as the latest run script.

So now we can merge with A branch. Once we merge with A branch, our migration scripts will again look like this:

  1. migration_1
  2. migration_5
  3. migration_12
  4. migration_13 (we added)
  5. migration_15 (came with developer1’s changes)
  6. migration_16

But this time since our _MigrationHistory table shows migration_12 as the latest run script when we run update-database command, it will start running from migration_13 and then migration_15 and then migration_16. So basically it runs all the migration scripts in the sequence with the proper order.

After doing this sometimes we may get a warning that says “unable to update the database and all” in that case all we need to do is add a resolve conflict script with ignore changes command. Code first generates binary data for the DB and used that one to publish. Sometimes it runs out of dates so running a resolve conflicts script with ignoring changes will refresh it.

Add-migration ResolveConflict -ignorechanges
And we’re done! We solved the issue.

It may be a little bit difficult to understand for beginners. Please give your valuable feedback/comments/questions about this article below. Please let me know how you like and understand this article and how I could improve it.

 

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories