41

Недавно я изменил приложение с помощью следующих для разработчика:Путаница EF Авто Миграции и Посев - посев каждый запуск программы

DropCreateDatabaseIfModelChanges<Context> 


Для использования:

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


В моей db context У меня есть:

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, где я просто использовал Add before с посевом на drop db (DropCreateDatabaseIfModelChanges).

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

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

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

ответ

58

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

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

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

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

+0

Спасибо Артур. Это было ясно, лаконично и просто то, что я искал. Я закончил тем, что изменил свое семя, чтобы предположить, что он будет работать каждый раз, но хорошо знать историю, стоящую за ней (которую я не смог найти). – dubbreak

+9

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

2

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

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

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

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

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

Спасибо, это именно то, что я искал. – Jono

+1

Отличное решение, работает как волшебство. – sairfan

Смежные вопросы