3

We want to support all of the following scenarios using a EF6 database initializer to create or update an existing database from our EF6 model:

  1. Create and initialize a new database.
  2. Update an existing (legacy) database that has no __MigrationHistory table yet.
  3. Update an existing (migrated) database that already has a __MigrationHistory table.

When using the CreateDatabaseIfNotExists database initializer, it covers only the first scenario. But then we cannot further evolve our data model.

When using the MigrateDatabaseToLatestVersion database initializer with an InitialDatabase migration that creates the base line database, we can support scenario 1 and 3. But it fails to upgrade existing legacy databases because the generated SQL statements are not idempotent. They fail e. g. with a "Table 'xy' already exists" error.

When using the MigrateDatabaseToLatestVersion database initializer with an empty InitialDatabase migration (=empty Up() method body), then we can only support scenario 2 and 3, but we cannot create new database from scratch.

I am looking for a way that combines the best of both worlds and supports all three required scenarios. Unfortunately this seems impossible with the current EF6 design. The problem I am facing is that the DbMigration steps are discovered using reflection and there seems to be no (clean) way to intercept this. What I would like to do is to write an enhanced "hybrid" database initializer that: 1. Checks if the database exists. 2. If not, then execute all migrations (=default behavior of MigrateDatabaseToLatestVersion). 3. Otherwise check if it is already enabled for migrations (i. e. table __MigrationHistory exists). 4. If yes, then execute only the pending migrations (=default behavior of MigrateDatabaseToLatestVersion). 5. Otherwise execute all DbMigrations except the very first "InitialDatabase" migration. This should also create the __MigrationHistory table and include all(!) migrations (including the very first "InitialDatabase" migration).

I don't find a way to implement step 5.

I would be happy with a "hack" that lets me just catch and ignore exceptions from upgrade operations of the first step (with a well-known MigrationId). I already thought about deriving a class from DbMigrator and override the ApplyMigration method with a

try 
{ 
  base.ApplyMigration(...) 
} 
catch 
{ 
  ...ignore if migrationMetadata.Id=="my well-known id"..
} 

block. But this is impossible because the ApplyMigration is internal.

Any ideas on this?

candritzky
  • 351
  • 4
  • 11
  • [This](https://stackoverflow.com/questions/15796115/how-to-create-initializer-to-create-and-migrate-mysql-database) might point you in the right direction. Also, you don't need Up() code for MigrateDatabaseToLatestVersion to create the database. In the console you can do `update-database -SourceMigration $InitialDatabase` and it will create one from scratch. – Steve Greene Jul 29 '17 at 14:24
  • I ended up using the `MigrateDatabaseToLatestVersion` strategy, and just added a call to `Database.CreateIfNotExists();` in my constructor. – BrainSlugs83 Nov 09 '18 at 22:51

0 Answers0