实体框架6代码优先迁移 - 从CreateDatabaseIfNotExists初始化程序

时间:2017-06-06 09:16:59

标签: sql-server database entity-framework

我已经启动了一个EF6项目来存储分析仪器的测量结果。每台仪器都有一台内置的PC,并且有自己的结果数据库。

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

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

为此,我想使用MigrateDatabaseToLatestVersion初始化程序。但我必须支持以下两种情况:

  1. 具有非唯一MigrationId的现有数据库,必须使用三个新表迁移到扩展版本。
  2. 没有数据库,使用MigrateDatabaseToLatestVersion初始化程序创建数据库。
  3. 我该怎么做?

    我使用初始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工作流程,那将会很有趣。

1 个答案:

答案 0 :(得分:0)

这是一个很常见的EF处理方式。非迁移初始值设定项(CreateDatabaseIfNotExists等)应该在很早的开发中使用(当你不关心数据时除了播种的东西)。

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

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

不确定您在使用MigrationId谈论什么。 EF会自动处理,除非您更改命名空间(此外还有解决方法)。