切换数据库后,DbMigrator不会检测挂起的迁移

PTw*_*Twr 5 c# entity-framework ef-code-first ef-migrations

切换到新上下文后,EntityFramework迁移变得无用.DbMigrator正在使用来自第一个数据库实例的Pending Migrations列表,这意味着没有迁移应用于其他数据库,然后在Seed()期间导致错误;

  • 使用EF 6的C#.NET 4.5 MVC项目
  • MS SQL Server 2014,相同数据库模型的多个实例.
  • 迁移的CodeFirst方法.
  • DbContext初始值设定项设置为null.

在Application Start上,我们有自定义Db初始化来创建和更新数据库.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);
    }
}
Run Code Online (Sandbox Code Playgroud)
  • context.Database.CreateIfNotExists(); 工作正常.
  • migrator.GetLocalMigrations() 总是返回正确的值.
  • migrator.GetPendingMigrations() 在第一个数据库返回空列表后.
  • migrator.GetDatabaseMigrations() 是挂起的迁移的镜像,在第一个数据库之后它包含空数据库的完整列表事件.
  • context.xxx.ToList()从Db实例获取data()确认连接已启动并正常工作,并链接到正确的实例.

强制更新到最近的迁移,migrator.Update("migration_name");不做任何更改.从我通过阅读EF源代码收集的内容中,它自己检查挂起的迁移列表,这会给它带来错误的结果.

似乎有一些缓存在幕后,但它让我无法重置它.

有没有办法在多个数据库上执行迁移,还是EF中的另一个"设计错误"?


编辑:

真正的问题是DbMigrator为自己的用途创建新的Context.它是通过默认的无参数构造函数来实现的,在我的例子中,它有回退到默认(第一个)连接字符串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
Run Code Online (Sandbox Code Playgroud)

作弊来自:如何在.net中以编程方式设置连接字符串配置?


我仍然希望有人会找到真正的解决方案,所以现在我将克制自我回答.

Iva*_*oev 5

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

所以理论上你可以做这样的事情

conf.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(...);
Run Code Online (Sandbox Code Playgroud)

不幸的是,只有2个公共构造函数DbConnectionInfo

public DbConnectionInfo(string connectionName)
Run Code Online (Sandbox Code Playgroud)

connectionName:应用程序配置中连接字符串的名称。

public DbConnectionInfo(string connectionString, string providerInvariantName)
Run Code Online (Sandbox Code Playgroud)

connectionString:用于连接的连接字符串。
providerInvariantName:用于连接的提供程序的名称。对 SQL Server 使用“System.Data.SqlClient”。

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

更新:我没有找到一种很好的“官方”获取所需信息的方式,所以我已经结束了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);
Run Code Online (Sandbox Code Playgroud)