DbMigrator 在切换数据库后未检测到挂起的迁移

DbMigrator does not detect pending migrations after switching database

EntityFramework 迁移到新上下文后变得无用。 DbMigrator 使用来自第一个数据库实例的待处理迁移列表,这意味着没有迁移应用于其他数据库,这会导致在 Seed();

期间出错

在应用程序启动时,我们有自定义数据库初始化来创建和更新数据库。 CreateDatabaseIfNotExists 正在按预期工作,新数据库已应用所有迁移。但是 MigrateDatabaseToLatestVersion 初始化器和我们的自定义初始化器都无法更新列表中第一个以外的数据库。

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);
    }
}

使用 migrator.Update("migration_name"); 强制更新到最近的迁移没有任何改变。根据我通过阅读 EF 源代码收集到的信息,它会自行检查挂起的迁移列表,这会给出错误的结果。

引擎盖下似乎有一些缓存,但我不知道如何重置它。

有没有办法在多个数据库上执行迁移,或者它是另一个 "bug by design" 在 EF 中?


编辑:

真正的问题是 DbMigrator 创建新的上下文供自己使用。它通过默认的无参数构造函数来实现,在我的例子中,它回退到 web.Config.

中的默认(第一个)连接字符串

我没有看到这个问题的好的解决方案,但我的原始解决方法是临时编辑默认连接字符串:

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

作弊来自:How do I set a connection string config programatically in .net?


我仍然希望有人能找到真正的解决方案,所以现在我不会自我回答。

您需要正确设置DbMigrationsConfiguration.TargetDatabase属性,否则迁移器将使用默认连接信息。

所以理论上你可以这样做

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

不幸的是 DbConnectionInfo 中仅有的 2 public constructors

public DbConnectionInfo(string connectionName)

connectionName: The name of the connection string in the application configuration.

public DbConnectionInfo(string connectionString, string providerInvariantName)

connectionString: The connection string to use for the connection.
providerInvariantName: The name of the provider to use for the connection. Use 'System.Data.SqlClient' for SQL Server.

我看到你有连接字符串,但不知道如何获得 providerInvariantName

更新: 我没有找到一个好的 "official" 获取所需信息的方法,所以我已经结束了使用 hack 访问 internal 成员通过反射,但在我看来,它比你使用的更安全:

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);