2017-06-06 82 views
0

我已经启动了一个EF6项目来存储分析仪器的测量结果。每台仪器都有一个内置的PC和它自己的结果数据库。Entity Framework 6代码优先迁移 - 从CreateDatabaseIfNotExists初始化程序开始

最初,使用了数据库初始化程序CreateDatabaseIfNotExists。创建数据库时,它会在__MigrationHistory表中创建一个条目,其中包含一个非唯一的MigrationId条目(时间戳因仪器而异,例如201706011336597_InitialCreate),ContextKey(如果我的派生DbContext的完全限定类型)。

过了一段时间,决定将更多的结果数据添加到数据库中......幸运的是,只需要三个新表格。现有表格中没有更改。

为此,我想使用MigrateDatabaseToLatestVersion初始值设定项。但我必须支持以下两种情况:

  1. 具有非唯一MigrationId的现有数据库必须使用三个新表迁移到扩展版本。
  2. 没有数据库,用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工作流程。

回答

0

这是EF处理得很好的一种非常常见的情况。非迁移初始值设定项(CreateDatabaseIfNotExists等)应该在非常早期的开发中使用(当你不关心除种子之外的数据)。

切换到迁移后,您应该生成一个基线迁移,并根据您的指示获取当前模型的快照(add-migration MyStartPoint -IgnoreChanges)。这增加了一个没有Up()代码的迁移,并将代码的当前状态存储在第一个模型中,以便在更改模型时只反映这些更改。您可以通过注释Up()代码中存在的项目来完成同样的事情。

现在,当您针对现有数据库运行时,它将检查__MigrationHistory以查看应用了哪些迁移。如果数据库不存在,它将被创建。有关更多信息,请参阅herehere

不确定你在说什么与MigrationId。除非您更改名称空间,否则EF会自动处理该名称空间(还有一种解决方法)。

+0

感谢您的回复。我再次测试了工作流程,发现在使用PM控制台命令** Enable-Migrations **启用迁移时指定** - ContextTypeName **参数非常重要。结果,生成的Configuration类的'ContextKey'属性用我们派生的'DbContext'的完全限定名初始化。否则,生成的“Configuration”类的'ContextKey'属性将使用生成的'Configuration'类本身的全限定名进行初始化。然后,迁移不起作用。 – KBO

+0

解决方法之一是在构造函数中明确地给出一个contextkey。见[这里](https://msdn.microsoft.com/en-us/magazine/dn948104.aspx) –

+0

你是对的,解释(见给出的链接)澄清。我最后使用的** - ContextTypeName **参数具有类似的结果,即'DbMigrationsConfiguration'中的右边的'ContextKey'属性。问题是,'MigrateDatabaseToLatestVersion'类的默认行为不适用于以前使用的'CreateDatabaseIfNotExists'类。现在它工作...谢谢 – KBO