42

I recently changed an application from using the following for dev:

DropCreateDatabaseIfModelChanges<Context>


To using:

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


In my db context I have:

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

I have overridden Seed(context) in DbMigrationsConfiguration using the AddOrUpdate extension where I was just using Add before with seeding on the drop db (DropCreateDatabaseIfModelChanges).

My confusion is that the Migration runs with each start of the application regardless of there being any changes to the DbContext. Each and every time I start the application (library run through a service) the initializer runs as does the Seed. My expected behaviour is a check whether a migration is necessary (behind the scenes check to see if model matches physical db) then update any new/removed tables/columns and only run seed if something has changed.

In my testing seed runs every time, which is workable but seemingly inefficient and was not what I expected. Unfortunately the MSDN documentation is quite limited.

Am I completely misusing MigrateDatabaseToLatestVersion? Is there any way to get the behaviour I expect (i.e. only seed if there is a model change) or should I just change my seed method to expect to be run every application launch?

4

3 回答 3

61

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

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

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

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

于 2012-05-31T00:39:07.230 回答
18

我有点同意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();
    }
}
于 2015-01-16T19:23:50.627 回答
2

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

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

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

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

于 2012-10-14T12:41:03.680 回答