Путаница эф автоматической миграции и посев - посев каждом старте программы


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

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

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

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

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

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

3 42

3 ответа:

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

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

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

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

Я несколько согласен с ответом 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();
    }
}

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

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

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

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