我已经启动了一个EF6项目来存储分析仪器的测量结果。每台仪器都有一个内置的PC和它自己的结果数据库。Entity Framework 6代码优先迁移 - 从CreateDatabaseIfNotExists初始化程序开始
最初,使用了数据库初始化程序CreateDatabaseIfNotExists。创建数据库时,它会在__MigrationHistory表中创建一个条目,其中包含一个非唯一的MigrationId条目(时间戳因仪器而异,例如201706011336597_InitialCreate),ContextKey(如果我的派生DbContext的完全限定类型)。
过了一段时间,决定将更多的结果数据添加到数据库中......幸运的是,只需要三个新表格。现有表格中没有更改。
为此,我想使用MigrateDatabaseToLatestVersion初始值设定项。但我必须支持以下两种情况:
- 具有非唯一MigrationId的现有数据库必须使用三个新表迁移到扩展版本。
- 没有数据库,用MigrateDatabaseToLatestVersion初始化器创建数据库。
我该怎么做?
我已经使用初始DbContext中的add-migration PM控制台命令创建了初始迁移。这与方案2(没有数据库存在)很相称。从这个起点,我可以更新我的DbContext并使用三个新表创建一个新的迁移。
但是如何支持场景1?初始迁移的Up()方法包含表创建代码,这不是必要的,因为这些表已经存在。是一个空的迁移(add-migration -IgnoreChanges)有帮助,也许有比初始迁移更晚的时间戳?
注意:我无法从PM控制台访问目标数据库,只能在我的开发人员计算机上访问测试数据库。
感谢和问候
卡斯滕
更新: 我已经修改了创建初始迁移与静态标志TablesAlreadyCreated。
public partial class InitialMigraCreate : DbMigration
{
/// <summary>
/// Set this field to true, if the tables are already created by the
/// CreateDatabaseIfNotExists database initializer. Then, the Up()
/// and Down() methods do nothing, but the
/// migration is added to the __MigrationHistory table.
/// </summary>
public static bool TablesAlreadyCreated = false;
public override void Up()
{
if (TablesAlreadyCreated)
return;
// several CreateTable calls here
}
/// <inheritdoc/>
public override void Down()
{
if (TablesAlreadyCreated)
return;
// several Drop... calls here
}
}
我还实施了一个新的数据库初始化类,如下所示:
public class MigrateDatabaseToLatestVersionEx<TContext, TMigrationsConfiguration> : IDatabaseInitializer<TContext>
where TContext : DbContext
where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
{
...
/// <inheritdoc />
public virtual void InitializeDatabase(TContext context)
{
if (context == null)
throw new ArgumentNullException("context");
// check whether a first migration exists from the CreateDatabaseIfNotExists database initializer
var firstConfig = new ConfigurationAutoCreatedDatabase();
firstConfig.TargetDatabase = _config.TargetDatabase;
var firstMigrator = new DbMigrator(firstConfig);
var firstDbMigrations = firstMigrator.GetDatabaseMigrations();
// create the default migrator with the current configuration
var migrator = new DbMigrator(_config);
if (1 == firstDbMigrations.Count(migra => migra.EndsWith("_InitialCreate", StringComparison.InvariantCultureIgnoreCase)))
{ // This database was created using the CreateDatabaseIfNotExists database initializer.
// That's an indication whether it's an old database
// Do the custom migration here!
InitialMigraCreate.TablesAlreadyCreated = true;
migrator.Update();
}
else
{ // do the default migration the database was created with this MigrateDatabaseToLatestVersionEx initializer
InitialMigraCreate.TablesAlreadyCreated = false;
migrator.Update();
}
}
}
它检查,初始迁移条目是从CreateDatabaseIfNotExists初始化和禁用表创建/删除通话在这种情况下,Up()/ Down()方法。 ConfigurationAutoCreatedDatabase是手动创建的派生类DbMigrationsConfiguration:
internal sealed class ConfigurationAutoCreatedDatabase : DbMigrationsConfiguration<MyNamespace.MyDbContext>
{
/// <summary>
/// Creates a <c>ConfigurationAutoCreated</c> object (default constructor).
/// </summary>
public ConfigurationAutoCreatedDatabase()
{
this.AutomaticMigrationsEnabled = false;
this.AutomaticMigrationDataLossAllowed = false;
this.ContextKey = "MyNamespace.MyDbContext";
}
}
所以,它适用于这两种方案。我希望能帮助其他有类似问题的人。有趣的是,如果该任务有一个开箱即用的EF工作流程。
感谢您的回复。我再次测试了工作流程,发现在使用PM控制台命令** Enable-Migrations **启用迁移时指定** - ContextTypeName **参数非常重要。结果,生成的Configuration类的'ContextKey'属性用我们派生的'DbContext'的完全限定名初始化。否则,生成的“Configuration”类的'ContextKey'属性将使用生成的'Configuration'类本身的全限定名进行初始化。然后,迁移不起作用。 – KBO
解决方法之一是在构造函数中明确地给出一个contextkey。见[这里](https://msdn.microsoft.com/en-us/magazine/dn948104.aspx) –
你是对的,解释(见给出的链接)澄清。我最后使用的** - ContextTypeName **参数具有类似的结果,即'DbMigrationsConfiguration'中的右边的'ContextKey'属性。问题是,'MigrateDatabaseToLatestVersion'类的默认行为不适用于以前使用的'CreateDatabaseIfNotExists'类。现在它工作...谢谢 – KBO