¿Cómo crear un inicializador para crear y migrar la base de datos mysql?

He estado aprendiendo a utilizar EF durante una semana más o menos y estoy atascado en el tema de la creación / actualización de mi base de datos. Puedo crear un inicializador para crear la base de datos si no está allí:

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

O puedo crear una configuración para migrar el archivo db

 static class Program { static void Main() { Database.SetInitializer(new MigrateDatabaseToLatestVersion()); .... internal sealed class Configuration : DbMigrationsConfiguration { public Configuration() { AutomaticMigrationsEnabled = true; SetSqlGenerator("MySql.Data.MySqlClient", new MySql.Data.Entity.MySqlMigrationSqlGenerator()); } protected override void Seed(GumpDatabase context) { } 

Cada uno funciona correctamente pero no he encontrado una manera de hacer ambas cosas. Puedo cambiar entre los dos inicializadores cambiando la llamada de SetInitializer pero si quiero crear la base de datos si no está allí y también migrarla si es lo que hago? ¿Necesito crear un inicializador personalizado?

Gracias

Edición basada en la respuesta NSGaga

 class CreateOrMigrateDatabaseInitializer : CreateDatabaseIfNotExists, IDatabaseInitializer where TContext : DbContext where TConfiguration : DbMigrationsConfiguration, 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.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) { } } 

y

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

y

 class GumpDatabaseInitializer : CreateOrMigrateDatabaseInitializer { 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)"); } } 

y finalmente

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

Creo que estás más o menos allí: puedes buscar el código fuente para MigrateDatabaseToLatestVersion (es de código abierto http://entityframework.codeplex.com/ ); es bastante simple, lo que hace es llamar DbMigrator , en la medida de lo posible. Podría ver.

Todo lo que tiene que hacer es fusionar los dos: use uno u otro como base, agregue otras funcionalidades, creo que debería funcionar bien.

 class CreateAndMigrateDatabaseInitializer : CreateDatabaseIfNotExists, IDatabaseInitializer where TContext : DbContext where TConfiguration : DbMigrationsConfiguration, 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.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) { } } 

llámalo así …

 Database.SetInitializer(new CreateAndMigrateDatabaseInitializer()); 

… en realidad, anularlo (ya que es una implementación genérica) como lo hacía para CreateDatabaseIfNotExists (solo tiene ‘param’ adicional para Configuration) – y simplemente suministrar el ‘Seed’.

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

… y llámalo algo así como

 Database.SetInitializer(new GumpDatabaseInitializer()); 

EDITAR: Basado en los comentarios, DbMigrator no debería ejecutarse dos veces. Siempre revisa (pasa un poco de tiempo) y hace una actualización ‘en blanco’ y continúa. Sin embargo, en caso de que quiera eliminarlo y ‘verificar’ antes de ingresar, esto debería funcionar (cambie la pieza similar anterior) …

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

(Esto es una verificación doble / redundante – uno de los if-s debería ser suficiente. Ponga un descanso allí y vea exactamente lo que está sucediendo, no debería entrar) una vez que se migra el Db. Como mencioné, funciona bien cuando Pruébalo.

EDITAR:

Reemplace el interior de InitializeDatabase con …

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

Esto funciona alrededor (a mitad de camino) no sembrado, si la migración va primero. Y las migraciones tienen que ser las primeras, de lo contrario, tienes problemas.

Aún necesita hacerlo correctamente, esta es la esencia, si no todo lo que pueda necesitar, pero si hay algún problema con MySQL, etc., probablemente tenga que trabajar un poco más aquí.

Nota: Still seeding no llama si tiene un db, pero está vacío. El problema es mezclar los dos inicializadores diferentes. Así que tendrás que resolverlo, ya sea implementando lo que Create … hace adentro (esa llamada a la que no podemos llamar) u otra cosa.

En realidad, debería ser:

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

porque si tenemos una migración, que no está relacionada con nuestro modelo db, por ejemplo, al insertar una fila en cualquiera de nuestras tablas, la migración no se ejecutará.

Para hacer ambas cosas (inicializar y migrar), solo tiene que usar migraciones con un inicializador MigrateDatabaseToLatestVersion . Cuando habilita las migraciones para su contexto, se crea una clase de Configuration derivada de DbMigrationsConfiguration y puede anular el método Seed para inicializar su base de datos. Tenga en cuenta que la base de datos puede contener ya datos iniciales cuando se ejecuta este método, pero el método de extensión AddOrUpdate ayuda a hacer “sugerencias” en su base de datos.

Esto es diferente en comparación con el método Seed de algunos de los otros iniciadores de bases de datos donde la base de datos solo se crea cuando se crea inicialmente. Sin embargo, cuando esté utilizando migraciones, es posible que desee cambiar sus datos iniciales cuando la base de datos cambie y el uso de MigrateDatabaseToLatestVersion hace posible.

Para combinar siembra con migraciones, deberá realizar los siguientes pasos en un nuevo proyecto:

  1. Crear un DbContext primero con DbContext con entidades asociadas

  2. En la consola del administrador de paquetes, ejecute el comando Enable-Migrations

  3. En la carpeta Migrations , se genera una clase de Configuration con un método Seed . Puede modificar este método para inicializar su base de datos:

     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 crear un inicializador de base de datos que derive de MigrateDatabaseToLatestVersion :

     class MyContextInitializer : MigrateDatabaseToLatestVersion { } 

    También deberá configurar el inicializador llamando a Database.SetInitializer(new MyContextInitializer()) cuando la aplicación se inicie o en el archivo App.config utilizando el elemento .

  5. En el constructor para la clase de Configuration generada puede habilitar migraciones automáticas:

     public Configuration() { AutomaticMigrationsEnabled = true } 

    Sin embargo, en un equipo es posible que prefiera no hacer eso. En ese caso, deberá crear una migración inicial (a menos que se haya creado al Enable-Migrations ). En el administrador de paquetes, ejecute el comando Add-Migration InitialCreate . Esto crea la primera migración requerida para crear su base de datos.

En este punto, tiene un DbContext con migraciones y un método Seed .

Por lo tanto, para resumirlo: habilite las migraciones, use el inicializador MigrateDatabaseToLatestVersion y agregue los datos iniciales en la clase de Configuration que se generó cuando se habilitaron las migraciones.

Mientras que MigrateDatabaseToLatestVersion realmente crea el DB si no existe e incluso le permite inicializarlo , si ya tiene una solución de trabajo basada en CreateDatabaseIfNotExists y / o no quiere complicarla con la prueba de la existencia de datos de inicialización, puede simplemente use el siguiente heredando de él en lugar de CreateDatabaseIfNotExists :

 public class CreateOrMigrateDatabaseInitializer : CreateDatabaseIfNotExists, IDatabaseInitializer where TContext : DbContext where TConfiguration : DbMigrationsConfiguration, new() { void IDatabaseInitializer.InitializeDatabase(TContext context) { if (context.Database.Exists()) { if (!context.Database.CompatibleWithModel(throwIfNoMetadata: false)) { var migrationInitializer = new MigrateDatabaseToLatestVersion(true); migrationInitializer.InitializeDatabase(context); } } base.InitializeDatabase(context); } } 

Esto se basa en respuestas anteriores y en la propia solución de OP. Esto debería funcionar también con otros proveedores, pero solo probé con SQL Server.