【问题标题】:Confusion over EF Auto Migrations and seeding - seeding every program start对 EF 自动迁移和播种的困惑 - 播种每个程序启动
【发布时间】:2012-06-05 01:29:20
【问题描述】:

我最近更改了一个应用程序,将以下内容用于开发:

DropCreateDatabaseIfModelChanges<Context>


使用方法:

public class MyDbMigrationsConfiguration: DbMigrationsConfiguration<GrsEntities>
{
    public MyDbMigrationsConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }
}


在我的数据库上下文中,我有:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Tell Code First to ignore PluralizingTableName convention
    // If you keep this convention then the generated tables will have pluralized names.
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

    //set the initializer to migration
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<GrsEntities, MigrationConfig>());
}

我已经使用 AddOrUpdate 扩展覆盖了 DbMigrationsConfiguration 中的 Seed(context),而之前我只是使用 Add 并在 drop db (DropCreateDatabaseIfModelChanges) 上播种。

我的困惑是,无论 DbContext 是否有任何更改,迁移都会在应用程序的每次启动时运行。每次我启动应用程序(库通过服务运行)时,初始化程序都会像种子一样运行。我的预期行为是检查是否有必要进行迁移(在幕后检查模型是否与物理数据库匹配),然后更新任何新的/删除的表/列,并且只有在发生变化时才运行种子。

在我的测试中,种子每次都运行,这是可行的,但似乎效率低下,并不是我所期望的。不幸的是,MSDN 文档非常有限。

我是否完全滥用了 MigrateDatabaseToLatestVersion?有什么方法可以得到我期望的行为(即只有在模型发生变化时才播种)还是应该只更改我的种子方法以期望在每次应用程序启动时运行?

【问题讨论】:

    标签: entity-framework ef-code-first entity-framework-4.3 entity-framework-migrations


    【解决方案1】:

    Seed 方法仅在数据库更改时才运行这一事实对于 EF 4.1 中附带的数据库初始化程序来说是非常有限的。这是有限制的,因为有时您需要在不更改数据库的情况下更新种子数据,但要做到这一点,您必须人为地让数据库看起来发生了变化。

    使用 Migrations,Seed 的使用变得有点不同,因为它不能再假设数据库开始是空的——毕竟这就是 Migrations 的意义所在。因此,Migrations 中的 Seed 方法必须假设数据库存在并且其中可能已经有数据,但可能需要更新数据以考虑对迁移数据库所做的更改。因此使用了 AddOrUpdate。

    所以现在我们遇到了必须编写 Seed 以考虑现有数据的情况,这意味着实际上没有必要延续 EF 4.1 Seed 方法的限制,这样您就必须让它看起来像数据库已更改只是为了让 Seed 运行。因此,现在每次在应用程序域中首次使用上下文时都会运行 Seed。这不应该改变 Seed 的实施方式,因为它需要处理数据已经存在的情况。

    如果由于您拥有大量 Seed 数据而导致性能问题,那么通常很容易在 Seed 方法中添加检查以查询数据库以确定在执行之前需要完成多少工作。

    【讨论】:

    • 谢谢亚瑟。这很清楚,简洁,正是我想要的。我最终改变了我的种子,假设它每次都会运行,但很高兴知道它背后的历史(我无法找到)。
    • 种子方法过去只在迁移发生时运行,而现在种子方法在每次第一次使用上下文时运行,这一事实非常令人困惑。是否有任何地方的 MSDN 文章详细说明了此更改?
    【解决方案2】:

    我有点同意Arthur Vickers 的回复,但是 IMO Seed 是针对 DbMigrations 的,我不希望 Seed 方法每次都检查所有内容,例如如果我有 4 次迁移,那么我需要以某种方式测试必须播种哪些数据,并且至少还有 4 次数据库命中。 如果您仍然希望仅在应用迁移时运行 Seed 方法的行为,就像我一样,我提供了自己的 IDatabaseInitializer 策略实现

    public class CheckAndMigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>
        : IDatabaseInitializer<TContext>
        where TContext : DbContext
        where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
    {
        public virtual void InitializeDatabase(TContext context)
        {
            var migratorBase = ((MigratorBase)new DbMigrator(Activator.CreateInstance<TMigrationsConfiguration>()));
            if (migratorBase.GetPendingMigrations().Any())
                migratorBase.Update();
        }
    }
    

    【讨论】:

    • 谢谢,这正是我想要的。
    【解决方案3】:

    另一个选项可能是在运行时在种子方法中加载自定义数据库初始化程序类。然后,生产应用程序可以加载一个虚拟初始化程序,而开发应用程序可以加载真正的初始化程序。你可以使用 Unity/MEF

        // Unity Dependency Injection Prop
        [Dependency]
        property IMyInitializer initializer;
    
        protected override Seed(YourContextClass context)
        {
           initializer.Seed(context);
        }
    

    类似的东西。一旦在生产环境中设置了数据库,您就可以将初始化程序切换到虚拟的初始化程序,这将无济于事。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-07
      • 2014-08-14
      • 1970-01-01
      • 2020-08-27
      • 1970-01-01
      • 1970-01-01
      • 2017-04-11
      相关资源
      最近更新 更多