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

时间:2016-03-17 12:23:09

标签: c# entity-framework ef-code-first ef-migrations

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

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

在应用程序启动时,我们有自定义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);
    }
}
  • context.Database.CreateIfNotExists();正常工作。
  • migrator.GetLocalMigrations()始终返回正确的值。
  • 第一个数据库返回后,
  • migrator.GetPendingMigrations() 空列表。
  • migrator.GetDatabaseMigrations()是待定迁移的镜像, 在第一个数据库之后,它包含空数据库的完整列表事件。
  • 从Db实例中获取数据(context.xxx.ToList())确认连接已启动并正常工作,并链接到正确的实例。

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

引擎盖下似乎有一些缓存,但它让我无法重置它。

有没有办法在多个数据库上执行迁移,还是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?

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

1 个答案:

答案 0 :(得分:4)

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

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

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

不幸的是DbConnectionInfo中只有2个公开constructors

public DbConnectionInfo(string connectionName)
  

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

public DbConnectionInfo(string connectionString, string providerInvariantName)
  

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

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

更新:我没有找到一个很好的“官方”方式来获取所需的信息,所以我结束了使用黑客通过反射访问internal成员,但仍然IMO它比你使用的更安全:

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