DbMigrator does not detect pending migrations after database switch

EntityFramework transitions become useless after moving to a new context. DbMigrator uses a list of pending migrations from the first database instance, which means that no migrations are applied to other databases, which then leads to errors during Seed ();

  • C # .NET 4.5 MVC Project with EF 6
  • MS SQL Server 2014, multiple instances of the same database model.
  • CodeFirst with migrations.
  • The DbContext initializer is null.

At the beginning of the application, we have custom Db initialization for creating and updating databases. CreateDatabaseIfNotExists works as intended, the new databases apply all migrations. However, both the MigrateDatabaseToLatestVersion initializer and our user initializer cannot update databases other than the first in the list.

 foreach (var connectionString in connectionStrings) { using (var context = new ApplicationDbContext(connectionString)) { //Create database var created = context.Database.CreateIfNotExists(); var conf = new Workshop.Migrations.Configuration(); var migrator = new DbMigrator(conf); migrator.Update(); //initial values conf.RunSeed(context); } } 
  • context.Database.CreateIfNotExists(); works correctly.
  • migrator.GetLocalMigrations() always returns the correct values.
  • migrator.GetPendingMigrations() after returning the first database an empty list.
  • migrator.GetDatabaseMigrations() is a mirror of pending migrations; after the first database, it contains the full list event for empty databases.
  • Getting data ( context.xxx.ToList() ) from a Db instance confirms that the connection is up and running, and links to the correct instance.

Force update the latest migration using migrator.Update("migration_name"); nothing changes. From what I collect by reading the source code for EF, it checks the list of pending migration itself, which gives it erroneous results.

It seems that caching happens under the hood, but it eludes me like reset.

Is there a way to perform migrations in multiple databases or is this another “design error” in EF?


Edit:

The Real Problem DbMigrator creates a new Context for its own use. He does this with the constructor without parameters without parameters, which in my case had a rejection of the default connection string (first) in web.Config .

I don't see a good solution to this problem, but in my case, a primitive workaround is to temporarily change the default connection string:

 var originalConStr = WebConfigurationManager.ConnectionStrings["ApplicationDbContext"].ConnectionString; var setting = WebConfigurationManager.ConnectionStrings["ApplicationDbContext"]; var fi = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic); //disable readonly flag on field fi.SetValue(setting, false); setting.ConnectionString = temporaryConnectionString; //now it works //DO STUFF setting.ConnectionString = originalConStr; //revert changes 

Cheat from: How to configure connection string programmatically in .net?


I still hope that someone will find a real solution, so for now I will refrain from self-deception.

+5
source share
1 answer

You need to set the DbMigrationsConfiguration.TargetDatabase property correctly , otherwise the migrator will use the default connection information.

So, in theory, you can do something like this

 conf.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(...); 

Unfortunately, only 2 public DbConnectionInfo constructors are

 public DbConnectionInfo(string connectionName) 

connection_name : the name of the connection string in the application configuration.

and

 public DbConnectionInfo(string connectionString, string providerInvariantName) 

connectionString . Connection string to connect.
providerInvariantName : the name of the provider used to connect. Use "System.Data.SqlClient" for SQL Server.

I see that you have a connection string, but I have no idea how you can get providerInvariantName .

UPDATE: I did not find a good “official” way to get the necessary information, so I ended up using a hack with access to internal members via reflection, but still IMO is safer than what you used:

 var internalContext = context.GetType().GetProperty("InternalContext", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(context); var providerName = (string)internalContext.GetType().GetProperty("ProviderName").GetValue(internalContext); var conf = new Workshop.Migrations.Configuration(); conf.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(connectionString, providerName); 
+4
source

Source: https://habr.com/ru/post/1245228/


All Articles