Enity Framework : Unable to generate an explicit migration... with Code-based migration



The situation is :

  1. Per tenant db.
  2. Non-automatic Code-based based migration. The initial creation of the dbs is through Code First too. There are no preexisting dbs in the current scenario.
  3. The exact db is not known during the generation of migration script becase there are many. The only thing I have is code - the Initial migration script based on the model in the beginning and the model with some modifications.
  4. Existing dbs generated with initial script - everything works fine. Here is how I reproduce the problem :
  5. Run PS command : Add-Migration -Name Initial -StartUpProjectName MyApp.Web -ProjectName MyApp.Migrations -ConfigurationTypeName "MyApp.Migrations.MyMigrationConfiguration" -ConnectionString "Data Source=.\SQLEXPRESS;Database=demo-db;Trusted_Connection=False;User ID=x;Password=x" -ConnectionProviderName "System.Data.SqlClient" - Works Fine
  6. Run the app and the db is created. Stop the app. Make modification to the model.
  7. Run PS command : Add-Migration -Name MySecondMigration -StartUpProjectName MyApp.Web -ProjectName MyApp.Migrations -ConfigurationTypeName "MyApp.Migrations.MyMigrationConfiguration" -ConnectionString "Data Source=.\SQLEXPRESS;Database=demo-db;Trusted_Connection=False;User ID=x;Password=x" -ConnectionProviderName "System.Data.SqlClient" - Boom.
  8. Subtlety - The migrator does not even connect to MSSQL to check if the db even exists or has dbo.__MigrationHistory! I have goen further and turned off the MSSQL service! - It does not matter if I enter false db name or not or I turn off the entire MSSQL server - the error is the same and no connection is made. So how does it know a migration is pending when it does not check the database? I assume a bug.
  9. I have __dbo.MigrationHistory created already with the Initial migration in it.
  10. Error : Unable to generate an explicit migration because the following explicit migrations are pending: [201402121953301_Initial]. Apply the pending explicit migrations before attempting to generate a new explicit migration.

Here is my Configuration - nothing special :

public class MyMigrationConfiguration : DbMigrationsConfiguration<MyMigrationContext>
    public MyMigrationConfiguration()
        AutomaticMigrationsEnabled = false;
        AutomaticMigrationDataLossAllowed = false;
        MigrationsNamespace = "---";
        MigrationsDirectory = "---";

Here is the method with wich I create the dbs :

 public void CreateOrUpdateDb(string DbName)
                string connectionString = _connectionStringProvider.GetConnectionString(DbName);
                DbMigrationsConfiguration cfg = CreateMigrationsConfig(connectionString);
                cfg.AutomaticMigrationsEnabled = false;
                cfg.AutomaticMigrationDataLossAllowed = false;
                DbMigrator dbMigrator = new DbMigrator(cfg);                              
            catch (MigrationsException exception)
                _logger.Error(string.Format("Error creating database '{0}'",DbName), exception);

I have already searched and read whatever I can find on the web but most examples include the standart Configuration.cs and the like. Nobody explains why should I run Enable-Migrations because it creates Configuration.cs but I provide the Configuration class myself and do not need this other class.

Here is the exception stack :

> System.Data.Entity.Migrations.Infrastructure.MigrationsPendingException: Unable to generate an explicit migration because the following explicit migrations are pending: [201402121953301_Initial]. Apply the pending explicit migrations before attempting to generate a new explicit migration.
   at System.Data.Entity.Migrations.DbMigrator.Scaffold(String migrationName, String namespace, Boolean ignoreChanges)
   at System.Data.Entity.Migrations.Design.MigrationScaffolder.Scaffold(String migrationName, Boolean ignoreChanges)
   at System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldRunner.Scaffold(MigrationScaffolder scaffolder)
   at System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldRunner.Run()
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Scaffold(String migrationName, String language, String rootNamespace, Boolean ignoreChanges)
   at System.Data.Entity.Migrations.AddMigrationCommand.Execute(String name, Boolean force, Boolean ignoreChanges)
   at System.Data.Entity.Migrations.AddMigrationCommand.<>c__DisplayClass2.<.ctor>b__0()

Is there any way to force the migrator to make the script without telling me that there are some pending migrations even though there are not? This is 100% bug in EF but I do not know how to get around it.

11/5/2014 11:19:17 AM

Accepted Answer

We have found the answer - a smart colleague of mine and with the help of Reflector.

  1. Inside EntityFramework.dll there is the class
  2. System.Data.Entity.Migrations.DbMigrator with a method
  3. Scaffold(string migrationName, string @namespace, bool ignoreChanges)
  4. Which makes a call this.GetPendingMigrations
  5. Which method calls another method on another class HistoryRepository.CreateHistoryQuery(HistoryContext context, string contextKey = null) with the parameter contextKey == NULL
  6. But there inside this method this happens

    contextKey = !string.IsNullOrWhiteSpace(contextKey) ? contextKey.RestrictTo(this._contextKeyMaxLength) : this._contextKey;

and the contextKey is not null anymore. IT actually becomes the type(YourInheritedDbMigrationConfiguration).ToString()

Where is the problem? The problem lies in the fact that

  1. I use two different DbMigrationConfiguration classes
  2. because the migration assembly is separated from the core of the application which makes
  3. them two differently 'fully qualified named classes' which at the end
  4. result in two different ContextKeys in the dbo._MigrationHistory table for the DbMigrationConfiguration which is used for the generation of the migration script.

So when I make the initial migration script and start the site and the site applies the script it is ok but in the dbo._MigrationHistory the ContextKey is "MyDbConigClass1". After that when I try Add-Migration which uses another DbMigrationConfiguration class the ContextKeys does not match - BOOM!

The solution : Added the same ContextKey = "MyTenantDb" in both of my configuration classes and everything started to work again because the type names no longer got involved but the DbMigrator used my custom ContextKey :)

11/24/2014 11:29:32 PM

Related Questions


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow