Entity Framework 5 Migraciones: Configuración de una migración inicial y una semilla única de la base de datos


Tengo una aplicación MVC4 que he actualizado recientemente a Entity Framework 5 y estoy tratando de mover nuestra base de datos a usar migraciones desde el estilo de desarrollo de soltar y crear cada ejecución.

Esto es lo que he hecho en mi función de inicio de aplicación.

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

Ejecuté el comando Enable-Migrations en mi proyecto de repositorios y pensé que esto crearía un archivo de migración inicial, sin embargo, el único archivo que creó fue Configuration

Cuando elimino la base de datos la crea como se espera a través de código primero y semillas de la base de datos desde el archivo de configuración. En el archivo de configuración cambié todas las funciones Add() a AddOrUpdate()

Sin embargo, ejecuta la función seed en mi archivo Configuration cada vez que se inicia el sitio web y duplica todos los datos seed una y otra vez.

Imaginé que crearía un archivo initial migration ya que el blog que leí sugirió que lo haría y pude poner los datos de semilla allí, pero no lo hizo

¿Puede alguien explicar cómo debería estar configurando DB en código de modo que solo se siembra una vez?


ENLACE: La entrada del blog de migraciones que seguí


Mientras que esto es bastante interesante para usar EF migrate.exe Desde entonces he cambiado a usar roundhouse para ejecutar migraciones. Todavía uso EF para scaffold mis migraciones basadas en los modelos, pero escribí una pequeña aplicación de consola para escribir las migraciones en archivos SQL. Luego uso roundhouse para realizar las migraciones a través de mis scripts de compilación de rake. Hay un poco más de proceso involucrado, pero es mucho más estable que el uso de EF para realizar las migraciones sobre la marcha cuando se inicia la aplicación.

Author: Neil, 2012-11-22

4 answers

Este ha demostrado ser un post popular, así que lo he actualizado a la luz de los comentarios de otros. Lo principal a saber es que el método Seed en la clase de Configuración se ejecuta CADA vez que se inicia la aplicación, que no es lo que implica el comentario en el método template. Ver la respuesta de alguien en Microsoft a este post acerca de por qué es - gracias a Jason Learmouth para encontrar eso.

Si, como yo, solo desea ejecutar las actualizaciones de la base de datos si hay alguna pendiente migraciones entonces usted necesita hacer un poco más de trabajo. Puede averiguarlo si hay migraciones pendientes llamando a migrator.GetPendingMigrations (), pero tiene que hacerlo en el ctor ya que la lista de migraciones pendientes se borra antes de que se llame al método Seed. El código para implementar esto, que va en las migraciones.La clase de configuración es la siguiente:

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

Debo señalar que algunas personas han sugerido poner el código semilla en el código de migración 'up' real. Esto funciona, pero significa necesitas recordar poner el código semilla en cada nueva migración y es bastante difícil recordarlo, así que no haría eso. Sin embargo, si su semilla cambia con cada migración, entonces ese podría ser el buen camino a seguir.

 39
Author: Jon P Smith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:39

¿Podría agregar una migración manualmente y llenarla con el código de siembra que desee? En la consola del administrador de paquetes ejecute:

Add-Migration [Name]

A continuación, puede editar el archivo que se crea para usted en su carpeta de migraciones.

En mi proyecto realmente hago siembra como Richard, aunque en el método Semilla de la configuración de contexto. Realmente no tengo preferencia. Pero las migraciones deben ser más eficientes, ya que la aplicación no necesita verificar si las filas existen en la base de datos cuando el se inicia la aplicación. Solo hay la necesidad de comprobar si la migración se ha ejecutado, que debería ser más rápido.

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" },
        });

Estoy usando esto en Global.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>()
        );
    }
}
 6
Author: oldwizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 14:34:07

Esto es algo que me he preguntado en el pasado también. Tengo ciertas tablas en mi base de datos que se rellenan en mi evento Seed, y ahora solo compruebo si una de ellas está vacía dentro del método Seed. Si hay filas, el método Seed no se ejecuta. No es infalible, pero funciona.

 2
Author: Richard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-22 10:18:29

La respuesta a esta pregunta SO explica por qué Seed se ejecuta cada vez que se ejecuta la aplicación.

Utilizo el método Jon Smiths, pero he puesto la instrucción check for pending migrations en un bloque # if como este:

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

De esa manera, cuando estoy depurando, el método Seed siempre se ejecuta para repoblar mis datos seed, lo que es útil cuando elimino durante las pruebas, etc. pero no tengo el impacto de perf cuando estoy en libertad.

 2
Author: Jason Learmouth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:14