DbMigrator 在切换数据库后未检测到挂起的迁移
DbMigrator does not detect pending migrations after switching database
EntityFramework 迁移到新上下文后变得无用。
DbMigrator 使用来自第一个数据库实例的待处理迁移列表,这意味着没有迁移应用于其他数据库,这会导致在 Seed();
期间出错
- 带有 EF 6 的 C# .NET 4.5 MVC 项目
- MS SQL Server 2014,同一数据库模型的多个实例。
- CodeFirst 迁移方法。
- DbContext 初始值设定为 null。
在应用程序启动时,我们有自定义数据库初始化来创建和更新数据库。 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 源代码收集到的信息,它会自行检查挂起的迁移列表,这会给出错误的结果。
引擎盖下似乎有一些缓存,但我不知道如何重置它。
有没有办法在多个数据库上执行迁移,或者它是另一个 "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);
EntityFramework 迁移到新上下文后变得无用。 DbMigrator 使用来自第一个数据库实例的待处理迁移列表,这意味着没有迁移应用于其他数据库,这会导致在 Seed();
期间出错- 带有 EF 6 的 C# .NET 4.5 MVC 项目
- MS SQL Server 2014,同一数据库模型的多个实例。
- CodeFirst 迁移方法。
- DbContext 初始值设定为 null。
在应用程序启动时,我们有自定义数据库初始化来创建和更新数据库。 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 源代码收集到的信息,它会自行检查挂起的迁移列表,这会给出错误的结果。
引擎盖下似乎有一些缓存,但我不知道如何重置它。
有没有办法在多个数据库上执行迁移,或者它是另一个 "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);