对EF自动迁移和播种的困惑 - 为每个程序启动播种

时间:2012-05-30 19:05:11

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

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

DropCreateDatabaseIfModelChanges<Context>


使用:

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


 在我的db上下文中,我有:

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>());
}

我在DbMigrationsConfiguration中使用AddOrUpdate扩展覆盖了种子(上下文),我刚刚在drop db(DropCreateDatabaseIfModelChanges)上播种时使用了Add。

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

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

我是否完全滥用了MigrateDatabaseToLatestVersion?有没有办法获得我期望的行为(即只有模型更改时只有种子)或者我应该只更改我的种子方法以期望在每次应用程序启动时运行?

3 个答案:

答案 0 :(得分:60)

只有在数据库发生更改时才运行Seed方法这一事实对于EF 4.1中提供的数据库初始化程序非常有限。这是有限的,因为有时您需要更新种子数据而不更改数据库,但为了实现这一点,您必须人为地使其看起来像数据库已更改。

通过迁移,Seed的使用变得有点不同,因为不能再认为数据库是空的 - 这毕竟是迁移的重点。因此,迁移中的Seed方法必须假定数据库存在且可能已包含数据,但可能需要更新该数据以考虑对迁移的数据库所做的更改。因此使用AddOrUpdate。

所以现在我们有一种情况,必须编写种子来考虑现有数据,这意味着实际上没有必要使EF 4.1种子方法的局限性永久化,这样你就不得不让它看起来像数据库已经改变,只是为了让Seed运行。因此,每次在app域中首次使用上下文时,Seed都会运行。这不应该改变Seed的实现方式,因为它需要处理数据已经存在的情况。

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

答案 1 :(得分:16)

我有点同意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();
    }
}

答案 2 :(得分:2)

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

    // Unity Dependency Injection Prop
    [Dependency]
    property IMyInitializer initializer;

    protected override Seed(YourContextClass context)
    {
       initializer.Seed(context);
    }

这样的事情。然后,一旦在生产中设置了数据库,就可以切换初始化程序,而无需执行任何操作的虚拟数据库。