2013-04-03 5 views
14

Я изучаю, как использовать EF в течение недели или около того сейчас, и я встал на вопрос о создании/обновлении моей базы данных. Я могу создать инициализатор, чтобы создать базу данных, если она не существует:Как создать инициализатор для создания и миграции базы данных mysql?

static class Program 
{ 
    static void Main() 
    { 
     Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer()); 
.... 

class GumpDatabaseInitializer : CreateDatabaseIfNotExists<GumpDatabase> 
{ 
    public GumpDatabaseInitializer() 
    { 
    } 
    protected override void Seed(GumpDatabase context) 
    { 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); 
     // Other stuff 
    } 
} 

Или я могу создать конфигурацию для миграции БД

static class Program 
{ 
    static void Main() 
    { 
     Database.SetInitializer<GumpDatabase>(new MigrateDatabaseToLatestVersion<GumpDatabase, Configuration>()); 
.... 

internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = true; 
     SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); 
    } 

    protected override void Seed(GumpDatabase context) 
    { 

    } 

Каждый работает правильно, но я не понял, чтобы сделать то и другое. Я могу переключаться между двумя инициализаторами, меняя вызов SetInitializer, но если я хочу создать базу данных, если ее нет, а также перенести ее, если это то, что я делаю? Нужно ли создавать пользовательский инициализатор?

Благодаря

Редактировать на основе NSGaga ответить

class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
    where TContext : DbContext 
    where TConfiguration : DbMigrationsConfiguration<TContext>, new() 
{ 
    private readonly DbMigrationsConfiguration _configuration; 
    public CreateOrMigrateDatabaseInitializer() 
    { 
     _configuration = new TConfiguration(); 
    } 
    public CreateOrMigrateDatabaseInitializer(string connection) 
    { 
     Contract.Requires(!string.IsNullOrEmpty(connection), "connection"); 

     _configuration = new TConfiguration 
     { 
      TargetDatabase = new DbConnectionInfo(connection) 
     }; 
    } 
    void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context) 
    { 
     Contract.Requires(context != null, "context"); 

     if (context.Database.Exists()) 
     { 
      if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
      { 
       var migrator = new DbMigrator(_configuration); 
       migrator.Update(); 
      } 
     } 
     else 
     { 
      context.Database.Create(); 
      Seed(context); 
      context.SaveChanges(); 
     } 


    } 
    protected virtual void Seed(TContext context) 
    { 
    } 
} 

и

internal sealed class Configuration : DbMigrationsConfiguration<GumpDatabase> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = true; 
     AutomaticMigrationDataLossAllowed = false; 
     SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); 
    } 

    protected override void Seed(GumpDatabase context) 
    { 
    } 
} 

и

class GumpDatabaseInitializer : CreateOrMigrateDatabaseInitializer<GumpDatabase,Gump.Migrations.Configuration> 
{ 
    public GumpDatabaseInitializer() 
    { 
    } 
    protected override void Seed(GumpDatabase context) 
    { 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Sequences (Name)"); 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX StationPartNumber ON StationPartNumbers (StationId,PartNumberId)"); 
    } 
} 

и, наконец,

static void Main() 
{ 
    Database.SetInitializer<GumpDatabase>(new GumpDatabaseInitializer()); 

ответ

16

Я думаю, что вы очень много есть - вы можете поиск исходного кода для MigrateDatabaseToLatestVersion (это с открытым исходным кодом http://entityframework.codeplex.com/) - это довольно упрощенно, что он делает очень многое назвать DbMigrator - насколько как я мог видеть.

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

class CreateAndMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
    where TContext : DbContext 
    where TConfiguration : DbMigrationsConfiguration<TContext>, new() 
{ 
    private readonly DbMigrationsConfiguration _configuration; 
    public CreateAndMigrateDatabaseInitializer() 
    { 
     _configuration = new TConfiguration(); 
    } 
    public CreateAndMigrateDatabaseInitializer(string connection) 
    { 
     Contract.Requires(!string.IsNullOrEmpty(connection), "connection"); 

     _configuration = new TConfiguration 
     { 
      TargetDatabase = new DbConnectionInfo(connection) 
     }; 
    } 
    void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context) 
    { 
     Contract.Requires(context != null, "context"); 

     var migrator = new DbMigrator(_configuration); 
     migrator.Update(); 

     // move on with the 'CreateDatabaseIfNotExists' for the 'Seed' 
     base.InitializeDatabase(context); 
    } 
    protected override void Seed(TContext context) 
    { 
    } 
} 

вызова это так ...

Database.SetInitializer(new CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration>()); 

... на самом деле, переопределить его (так как это общая реализация), как вы делали для CreateDatabaseIfNotExists (вы просто дополнительных Пар "для конфигурации) - и просто поставьте «Семя».

class GumpDatabaseInitializer : CreateAndMigrateDatabaseInitializer<GumpDatabase, YourNamespace.Migrations.Configuration> 
{ 
    protected override void Seed(GumpDatabase context) 
    { 
     context.Database.ExecuteSqlCommand("CREATE UNIQUE INDEX Name ON Stations (Name)"); 
    } 
} 

... и назвать это что-то вроде

Database.SetInitializer(new GumpDatabaseInitializer()); 

EDIT: На основе комментариев - DbMigrator не должен работать в два раза. Он всегда проверяет (тратит немного времени) и делает «пустое» обновление и движется дальше. Однако только в том случае, если вы хотите удалить, что и «проверить» перед входом - это должно работать (изменить подобную часть выше) ...

var migrator = new DbMigrator(_configuration); 
if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
    if (migrator.GetPendingMigrations().Any()) 
     migrator.Update(); 

(это избыточный/перепроверить - один из if-s должно быть достаточно. Положите разрыв - и посмотрите, что именно происходит, он не должен входить - как только Db будет перенесен. Как я уже упоминал, отлично работает, когда я его тестирую.

EDIT:

Заменить внутри InitializeDatabase с ...

var doseed = !context.Database.Exists(); 
// && new DatabaseTableChecker().AnyModelTableExists(context); 
// check to see if to seed - we 'lack' the 'AnyModelTableExists' - could be copied/done otherwise if needed... 

var migrator = new DbMigrator(_configuration); 
// if (doseed || !context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
    if (migrator.GetPendingMigrations().Any()) 
     migrator.Update(); 

// move on with the 'CreateDatabaseIfNotExists' for the 'Seed' 
base.InitializeDatabase(context); 
if (doseed) 
{ 
    Seed(context); 
    context.SaveChanges(); 
} 

Это работает вокруг (на полпути) не посева - если миграция идет первым. Первыми должны быть миграции, иначе у вас есть проблемы.

Вам все равно нужно сделать это правильно - это суть, если не все, что вам может понадобиться, но если какие-либо проблемы с MySQL и т. Д., Возможно, здесь работает еще одна работа.

Примечание: Все еще посев не вызывается, если у вас есть дБ, но он пуст. Проблема заключается в смешении двух разных инициализаторов. Таким образом, вам нужно будет это решить - либо путем реализации того, что Create ... делает внутри (этот вызов мы не можем назвать), либо что-то еще.

+0

Не могли бы вы узнать, где получить источник для EF 4.3.1? У сайта Microsoft только 5 или 6. – Matt

+0

Я думаю, что они пошли «с открытым исходным кодом» только для новых обновлений (т. Е. EF 6), но источник должен быть одинаков - по крайней мере, по крайней мере. Если у вас есть 'DbMigrator', это в значительной степени это – NSGaga

+0

Похоже, что есть некоторые недостающие части:' using System.Data.Entity.Config; с использованием System.Data.Entity.Internal; с использованием System.Data.Entity.Resources; с использованием System.Data.Entity.Utilities; 'не найден в EF 4.3.1. – Matt

1

На самом деле это должно быть:

var migrator = new DbMigrator(_configuration); 
if (!context.Database.CompatibleWithModel(false) || migrator.GetPendingMigrations().Any()) 
    migrator.Update(); 

, потому что если у нас есть миграции, которая не связана с нашей моделью БД, например, вставив строку в любом из наших таблиц, миграция не будет выполнена ,

1

Для того чтобы сделать (семя и миграцию), вам действительно нужно использовать миграции с помощью инициализатора MigrateDatabaseToLatestVersion. Когда вы включаете миграции для своего контекста, создается класс Configuration, полученный из DbMigrationsConfiguration, и вы можете переопределить метод Seed для засеивания вашей базы данных. Обратите внимание, что база данных может уже содержать данные семени, когда этот метод выполняется, но метод расширения AddOrUpdate удобно помогает вам создавать «upserts» в вашей базе данных.

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

Чтобы объединить высев с миграциями вам придется выполнить следующие шаги в новом проекте:

  1. Создание кода первая DbContext с соответствующими субъектами

  2. В консоли менеджера пакетов выполните команда Enable-Migrations

  3. В папке MigrationsConfiguration класс генерируется с Seed. Вы можете изменить этот метод для семян базы данных:

    protected override void Seed(MyContext context) { 
        // Add two entities with name "Foo" and "Bar". 
        context.MyEntities.AddOrUpdate(
        e => e.Name, 
        new MyEntity { Name = "Foo" }, 
        new MyEntity { Name = "Bar" } 
    ); 
    } 
    
  4. Вам необходимо создать базу данных инициализатора, производный от MigrateDatabaseToLatestVersion:

    class MyContextInitializer 
        : MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration> { } 
    

    Вы также должны настроить инициализатору либо по телефону Database.SetInitializer(new MyContextInitializer()) когда вы запускаете приложение или в файле App.config с помощью элемента <databaseInitializer/>.

  5. В конструкторе для генерируемого Configuration класса можно включить автоматическую миграцию:

    public Configuration() { 
        AutomaticMigrationsEnabled = true 
    } 
    

    Однако в команде вы можете предпочесть, чтобы не делать этого. В этом случае вам придется создать начальную миграцию (если она не была создана, когда вы сделали Enable-Migrations). В диспетчере пакетов выполните команду Add-Migration InitialCreate. Это создает первую миграцию, необходимую для создания вашей базы данных.

На данный момент у вас есть DbContext с миграциями и метод Seed.

Итак, чтобы рассчитать это значение: Включите миграцию, используйте инициализатор MigrateDatabaseToLatestVersion и добавьте данные семени в класс Configuration, который был сгенерирован при включении миграции.

0

Хотя MigrateDatabaseToLatestVersion на самом деле создать БД, если она не существует, и даже позволяет семени его, если у вас уже есть рабочее решение, основанное на CreateDatabaseIfNotExists и/или не хотят усложнять его с тестированием для существования данных семян, вы можете просто использовать ниже, унаследовав от него, а не от CreateDatabaseIfNotExists:

public class CreateOrMigrateDatabaseInitializer<TContext, TConfiguration> : CreateDatabaseIfNotExists<TContext>, IDatabaseInitializer<TContext> 
     where TContext : DbContext 
     where TConfiguration : DbMigrationsConfiguration<TContext>, new() 
    { 

     void IDatabaseInitializer<TContext>.InitializeDatabase(TContext context) 
     { 
      if (context.Database.Exists()) 
      { 
       if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) 
       { 
        var migrationInitializer = new MigrateDatabaseToLatestVersion<TContext, TConfiguration>(true); 
        migrationInitializer.InitializeDatabase(context); 
       } 
      } 

      base.InitializeDatabase(context); 
     } 
    } 

Это основано на предыдущих ответов и собственное решение ФП в. Это должно работать и с другими провайдерами, но я тестировал только SQL Server.