26

У меня есть приложение MVC4, которое я недавно обновил до Entity Framework 5, и я пытаюсь переместить нашу базу данных на использование миграции из стиля разработки отбрасывания и создания каждого прогона.Entity Framework 5 Миграции: настройка начальной миграции и одиночное семя базы данных

Вот что я сделал в своей функции запуска приложения.

protected void Application_Start() 
{ 
    Database.SetInitializer(
     new MigrateDatabaseToLatestVersion< MyContext, Configuration >()); 
    ... 
} 

я выполнил команду Enable-Migrations над проектом репозиториев, и я подумал, что это создаст исходный файл миграции однако единственный файл, он создан был Configuration

При удалении базы данных, он создает ее, как ожидается, через сначала код и семена базы данных из файла конфигурации. В конфигурационном файле я изменил все Add() функции AddOrUpdate()

Однако запускает функцию семян в моем Configuration файл каждый раз при запуске веб-сайта и дублирует все данные семенных снова и снова.

я представлял себе, что это создаст initial migration файл в блоге я прочитал, что было бы, и я мог бы поместить данные в семенных там, но это не

Может кто-нибудь объяснить, как я должен быть создание БД в коде, чтобы он только семена раз?


LINK: The migrations blog post I followed


Хотя это довольно интересно для использования EF migrate.exe Я с тех пор перешел на использование roundhouse для выполнения миграции. Я по-прежнему использую EF для выравнивания моих миграций на основе моделей, но я написал небольшое консольное приложение, чтобы записать миграции в файлы SQL. Затем я использую roundhouse для выполнения самих миграций через мои скрипты сборки rake. Есть немного больше процесса, но он намного более стабилен, чем использование EF для выполнения миграции на лету, когда приложение запускается.

ответ

38

Это оказалось популярным сообщением, поэтому я обновил его в свете отзывов других. Главное знать, что метод Seed в классе Configuration запускается КАЖДОЕ время запуска приложения, что не соответствует комментарию в методе шаблона. См. Ответ от кого-то из Microsoft на этот post о том, почему это - благодаря Джейсону Лирмуту за то, что он нашел это.

Если вы, как и я, только хотите запустить обновления базы данных, если есть какие-либо ожидающие миграции, вам нужно немного поработать. Вы можете найти это, если есть ожидающие миграции, вызвав migrator.GetPendingMigrations(), но вы должны сделать это в ctor, так как список ожидающих миграции очищается до вызова метода Seed. Код для реализации этого, который идет в классе Migrations.Configuration выглядит следующим образом:

internal sealed class Configuration : DbMigrationsConfiguration<YourDbContext> 
{ 
    private readonly bool _pendingMigrations; 

    public Configuration() 
    { 
     // If you want automatic migrations the uncomment the line below. 
     //AutomaticMigrationsEnabled = true; 
     var migrator = new DbMigrator(this); 
     _pendingMigrations = migrator.GetPendingMigrations().Any(); 
    } 

    protected override void Seed(MyDbContext context) 
    { 
     //Microsoft comment says "This method will be called after migrating to the latest version." 
     //However my testing shows that it is called every time the software starts 

     //Exit if there aren't any pending migrations 
     if (!_pendingMigrations) return; 

     //else run your code to seed the database, e.g. 
     context.Foos.AddOrUpdate(new Foo { bar = true}); 
    } 
} 

Я должен отметить, что некоторые люди предложили помещать код семян в действительной «вверх» миграции коды. Это работает, но означает, что вам нужно помнить о том, чтобы поместить семенной код в каждую новую миграцию и его довольно трудно запомнить, поэтому я бы этого не сделал. Однако, если ваше семя изменяется с каждой миграцией, это может быть хорошим способом.

+0

Я предполагаю, что он запускается каждый раз, потому что это единственный способ, которым они могли бы это сделать, без фактического внедрения посева в фактическую миграцию. Если ваш исходный код находился внутри Up() миграции, он будет запускаться только один раз, и это более интуитивно для меня, так как обычно вы делаете некоторые изменения в db и хотите засеять некоторые данные в эту новую часть схемы. – FRoZeN

+0

If у вас нет никаких миграций, ваш метод семени никогда не будет запущен с использованием этого метода. Я тоже сейчас предпочитаю делать посев в процессе миграции - таким образом, я знаю, что он будет запускаться только один раз. – oldwizard

+0

Hi pcguru. Фактически у вас всегда есть миграция, поскольку исходная база данных считается переносом, т. Е. Она переносится из базы данных без наличия базы данных. Однако я могу видеть преимущество наличия семян в миграции. –

2

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

+1

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

+0

Я согласен, что это хакерское решение - это не выход, более «Если строк нет, тогда вставьте некоторые». Затем вы можете повторить его для дополнительных таблиц, которые вы можете заполнить при миграции. – Richard

6

Вы можете добавить миграцию вручную и заполнить все с нужным кодом посева. В пакете менеджер консоли запуска:

Add-Migration [Name] 

Вы можете редактировать этот файл, который создается для вас в вашей папке кочевок.

В моем проекте я действительно делаю сеяние, как Ричард, хотя в методе Seed контекстной конфигурации. У меня действительно нет предпочтений. Но миграция должна быть более эффективной, так как приложение не нуждается в проверке, существуют ли строки в базе данных при запуске приложения. Необходимо только проверить, была ли выполнена миграция, которая должна быть быстрее.

internal sealed class Configuration : DbMigrationsConfiguration<MyContext> 
{ 
    public Configuration() 
    { 
     // If you want automatic migrations as well uncomment below. 
     // You can use both manual and automatic at the same time, but I don't recommend it. 
     //AutomaticMigrationsEnabled = true; 
     //AutomaticMigrationDataLossAllowed = true; 
    } 

    protected override void Seed(MyContext context) 
    { 
     // This method will be called after migrating to the latest version. 

     // You can use the DbSet<T>.AddOrUpdate() helper extension method 
     // to avoid creating duplicate seed data. 

     context.FontFamilies.AddOrUpdate(
      f => f.Id, 
      new FontFamily { Id = 1, PcName = "Arial" }, 
      new FontFamily { Id = 2, PcName = "Times New Roman" }, 
     }); 

Я использую это в глобальном масштабе.asax:

public class MvcApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
     // Any migrations that haven't been applied before will 
     // automatically be applied on Application Pool restart 

     Database.SetInitializer<MyContext>(
      new MigrateDatabaseToLatestVersion<MyContext, 
      MyApp.Migrations.Configuration>() 
     ); 
    } 
} 
+0

Это должно быть отмечено как ответ. Подсказка - это перегрузка «AddOrUpdate» с проверкой первичного ключа. Я не узнал его в первый раз. – Sven

2

Ответ на вопрос this SO question объясняет, почему семя запускается каждый раз, когда приложение запускается.

Я использую метод Джон Smiths, но я поставил чек на ожидании кочевок заявления в блоке #if, как это:

#if (!DEBUG) 
      if (!_pendingMigrations) return; 
#endif 

Таким образом, когда я отладки метод Seed всегда работает заселить мой данные семени - полезны, когда я удаляю во время тестирования и т. д., но я не получаю перфоманс при выпуске.