Путаница над EF Auto Migrations и посев - посев при каждом запуске программы

Недавно я изменил приложение с помощью следующего для dev:

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

Я переопределил Seed (контекст) в DbMigrationsConfiguration, используя расширение AddOrUpdate, где я просто использовал Add before с посевом в drop db (DropCreateDatabaseIfModelChanges).

Мое недоразумение состоит в том, что Migration запускается с каждым запуском приложения независимо от каких-либо изменений в DbContext. Каждый раз, когда я запускаю приложение (библиотека запускается через службу), инициализатор работает так же, как и Seed. Мое ожидаемое поведение - это проверка необходимости миграции (за кадром проверяет, соответствует ли модель физическому db), затем обновлять любые новые/удаленные таблицы/столбцы и запускать только семя, если что-то изменилось.

В моем тестировании семена запускаются каждый раз, что является работоспособным, но, по-видимому, неэффективным и не то, что я ожидал. К сожалению, документация MSDN довольно ограничена.

Я полностью злоупотребляю MigrateDatabaseToLatestVersion? Есть ли способ получить ожидаемое поведение (т.е. Только семя, если есть изменение модели), или я должен просто изменить метод семени, чтобы ожидать запуска каждого запуска приложения?

Ответ 1

Тот факт, что метод Seed запускался только при изменении базы данных, был довольно ограниченным для инициализаторов базы данных, которые были отправлены в EF 4.1. Это было предельно, потому что иногда вам нужно было обновлять данные семян без изменения базы данных, но чтобы это произошло, вам пришлось искусственно заставить его казаться, что база данных изменилась.

С помощью Migrations использование Seed немного изменилось, потому что больше нельзя предполагать, что база данных пуста - такова точка Migrations в конце концов. Таким образом, метод Seed в Migrations должен предположить, что база данных существует и может уже иметь в ней данные, но эти данные, возможно, потребуется обновить, чтобы учесть изменения, внесенные в базу данных для Migrations. Следовательно, использование AddOrUpdate.

Итак, теперь у нас есть ситуация, когда Seed должен быть написан для учета существующих данных, а это означает, что нет необходимости увековечивать ограничения метода EF 4.1 Seed таким образом, чтобы вам казалось, что база данных была изменена только для того, чтобы запустить Seed. Следовательно Seed теперь запускается каждый раз, когда контекст используется в первый раз в домене приложения. Это не должно меняться, так как семя будет изменено, поскольку оно должно обрабатывать случай, когда данные уже присутствуют.

Если это вызывает первичную проблему, потому что у вас много данных Seed, то обычно легко добавлять проверки в метод Seed, которые запрашивают базу данных, чтобы определить, сколько работы нужно выполнить, прежде чем делать это.

Ответ 2

Я несколько согласен с ответом Артуром Виккером, однако 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

Другим вариантом может быть загрузка пользовательского класса инициализатора db во время выполнения в методе семян. Затем приложение "Производство" может загрузить фиктивный инициализатор, в то время как приложение-разработчик может загрузить реальный инициализатор. Вы можете использовать Unity/MEF

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

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

Что-то вроде этого. Затем вы переключаете инициализаторы, как только у вас есть настройка базы данных в Production, на фиктивный, что ничего не сделает.