Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Migraciones de *Code First* es la manera recomendada para evolucionar el esquema de la base de datos de su aplicación si utiliza el flujo de trabajo de *Code First*. Migraciones proporciona un conjunto de herramientas que permiten:
- Crear una base de datos inicial que funciona con el modelo de EF
- Generar migraciones para realizar un seguimiento de los cambios realizados en el modelo de EF
- Mantener actualizada la base de datos con esos cambios
En el siguiente tutorial se proporciona información general sobre Migraciones de Code First en Entity Framework. Puede realizar el tutorial completo o ir al tema que le interesa. Se tratan los temas siguientes:
Creación de un modelo inicial y una base de datos
Antes de empezar a usar Migraciones, se necesitan un proyecto y un modelo de Code First con los que trabajar. En este tutorial se va a usar el modelo canónico Blog y Post.
- Cree una nueva aplicación de consola MigrationsDemo
- Agregue la versión más reciente del paquete NuGet de EntityFramework al proyecto
- Herramientas –> Administrador de paquetes de biblioteca–> Consola del Administrador de paquetes
- Ejecute el comando Install-Package EntityFramework
- Agregue un archivo Model.cs con el código que se muestra a continuación. Este código define una sola clase Blog que conforma el modelo de dominio y una clase BlogContext que es el contexto de EF Code First
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
}
}
- Ahora que tenemos un modelo, es hora de usarlo para el acceso a los datos. Actualice el archivo Program.cs con el código que se muestra a continuación.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Ejecute la aplicación y verá que se ha creado automáticamente una base de datos MigrationsCodeDemo.BlogContext.
Habilitación de migraciones
Es hora de realizar más cambios en el modelo.
- Vamos a introducir una propiedad Url en la clase Blog.
public string Url { get; set; }
Si volviera a ejecutar la aplicación de nuevo, aparecería una excepción InvalidOperationException con el texto El modelo que respalda el contexto 'BlogContext' ha cambiado desde que se creó la base de datos. Considere la posibilidad de usar migraciones de Code First para actualizar la base de datos (http://go.microsoft.com/fwlink/?LinkId=238269).
Como sugiere la excepción, es hora de empezar a usar Code First Migrations. El primer paso es habilitar las migraciones para nuestro contexto.
Ejecute el comando Enable-Migrations en la consola del Administrador de paquetes
Este comando ha agregado una carpeta Migraciones al proyecto. Esta nueva carpeta contiene dos archivos:
La clase Configuration. Esta clase permite configurar el comportamiento de Migraciones en el contexto. En este tutorial se usa la configuración predeterminada. Puesto que hay un solo contexto de Code First en el proyecto, Enable-Migrations ha rellenado automáticamente el tipo de contexto al que se aplica esta configuración.
Una migración de InitialCreate. Esta migración se ha generado porque Code First ya había creado automáticamente una base de datos antes de que se habilitaran las migraciones. El código de esta migración construida automáticamente representa los objetos que ya se han creado en la base de datos. En nuestro caso, es la tabla Blog con las columnas BlogId y Name. El nombre de archivo incluye una marca de tiempo para facilitar el ordenamiento. Si la base de datos aún no se hubiera creado, esta migración InitialCreate no se hubiera agregado al proyecto. Por el contrario, la primera vez que llamemos a Add-Migration, el código para crear estas tablas se generaría mediante scaffolding para una nueva migración.
Varios modelos con la misma base de datos como destino
Al usar versiones anteriores a EF6, solo se puede usar un modelo de Code First para generar o administrar el esquema de una base de datos. Este es el resultado de una sola tabla _MigrationsHistory por base de datos sin forma de identificar qué entradas pertenecen a qué modelo.
A partir de EF6, la clase Configuration incluye una propiedad ContextKey. Esta actúa como identificador único para cada modelo de Code First. Una columna correspondiente de la tabla _MigrationsHistory permite que entradas de varios modelos compartan la tabla. De forma predeterminada, esta propiedad se establece en el nombre completamente calificado del contexto.
Generación y ejecución de migraciones
Migraciones Code First tiene dos comandos principales con los que va a familiarizarse.
- Add-Migration generará la siguiente migración basado en los cambios realizados en su modelo desde que se creó la última migración.
- Update-Database aplica las migraciones pendientes a la base de datos
Es necesario preparar una migración para encargarse de la nueva propiedad Url que hemos agregado. El comando Add-Migration permite poner un nombre a estas migraciones; a la nuestra la llamaremos AddBlogUrl.
- Ejecute el comando Add-Migration AddBlogUrl en la consola del Administrador de paquetes
- En la carpeta Migraciones ahora tenemos una nueva migración AddBlogUrl. El nombre de archivo de la migración está antepuesto con una marca de tiempo para ayudar con la ordenación.
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddBlogUrl : DbMigration
{
public override void Up()
{
AddColumn("dbo.Blogs", "Url", c => c.String());
}
public override void Down()
{
DropColumn("dbo.Blogs", "Url");
}
}
}
Ahora podríamos editar o agregar a esta migración, pero todo parece bastante bien. Vamos a usar Update-Database para aplicar esta migración a la base de datos.
- Ejecute el comando Update-Database en la consola del Administrador de paquetes
- Las migraciones de Code First compararán las migraciones que están en la carpeta Migraciones con aquellas que se han aplicado a la base de datos. Verá que es necesario aplicar la migración AddBlogUrl y ejecutarla.
La base de datos MigrationsDemo.BlogContext se ha actualizado para incluir la columna Url en la tabla Blogs.
Personalización de migraciones
Hasta ahora hemos generado y ejecutado una migración sin realizar ningún cambio. Ahora vamos a editar el código que se genera de forma predeterminada.
- Es hora de realizar algunos cambios más en el modelo: vamos a agregar una nueva propiedad Rating a la clase Blog
public int Rating { get; set; }
- También vamos a agregar una nueva clase Post
public class Post
{
public int PostId { get; set; }
[MaxLength(200)]
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
- Además agregaremos una colección Posts a la clase Blog para formar el otro extremo de la relación entre Blog y Post
public virtual List<Post> Posts { get; set; }
Vamos a usar el comando Add-Migration para que Migraciones de Code First genere su mejor intento de la migración. Vamos a llamar a esta migración AddPostClass.
- Ejecute el comando Add-Migration AddPostClass en la consola del Administrador de paquetes.
Migraciones de Code First hizo un muy buen trabajo al estructurar estos cambios, pero hay algunas cosas que es posible que queramos modificar:
- En primer lugar, vamos a agregar un índice único a la columna Posts.Title (adición en la línea 22 y 29 del código siguiente).
- También vamos a agregar una columna Blogs.Rating que no admite valores null. Si hay datos existentes en la tabla, se les asigna el valor predeterminado CLR del tipo de datos para la nueva columna (Rating es entero, por lo que sería 0). Pero queremos especificar un valor predeterminado de 3, para que las filas existentes en la tabla Blogs comiencen con una clasificación decente. (Puede ver el valor predeterminado especificado en la línea 24 del código siguiente)
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostClass : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Posts",
c => new
{
PostId = c.Int(nullable: false, identity: true),
Title = c.String(maxLength: 200),
Content = c.String(),
BlogId = c.Int(nullable: false),
})
.PrimaryKey(t => t.PostId)
.ForeignKey("dbo.Blogs", t => t.BlogId, cascadeDelete: true)
.Index(t => t.BlogId)
.Index(p => p.Title, unique: true);
AddColumn("dbo.Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
}
public override void Down()
{
DropIndex("dbo.Posts", new[] { "Title" });
DropIndex("dbo.Posts", new[] { "BlogId" });
DropForeignKey("dbo.Posts", "BlogId", "dbo.Blogs");
DropColumn("dbo.Blogs", "Rating");
DropTable("dbo.Posts");
}
}
}
La migración editada ya está lista, así que vamos a usar Update-Database para actualizar la base de datos. Esta vez vamos a especificar el indicador -Verbose para que puedas ver el SQL que ejecuta Code First Migrations.
- Ejecute el comando Update-Database –Verbose en la consola del Administrador de paquetes.
Movimiento de datos o SQL personalizado
Hasta ahora hemos examinado las operaciones de migración que no cambian ni mueven datos, y ahora vamos a ver algo que necesita mover algunos datos. Todavía no hay compatibilidad nativa con el movimiento de datos, pero podemos ejecutar algunos comandos SQL arbitrarios en cualquier punto del script.
- Vamos a agregar una propiedad Post.Abstract al modelo. Luego, vamos a rellenar previamente el elemento Abstract de publicaciones existentes con algún texto del inicio de la columna Content.
public string Abstract { get; set; }
Vamos a usar el comando Add-Migration para que Migraciones de Code First genere su mejor suposición de la migración para nosotros.
- Ejecute el comando Add-Migration AddPostAbstract en la consola del Administrador de paquetes.
- La migración generada se encarga de los cambios de esquema, pero además queremos rellenar previamente la columna Abstract con los 100 primeros caracteres del contenido de cada publicación. Lo podemos hacer si recurrimos a SQL y ejecutamos una instrucción UPDATE después de agregar la columna. (Adición en la línea 12 del código siguiente)
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddPostAbstract : DbMigration
{
public override void Up()
{
AddColumn("dbo.Posts", "Abstract", c => c.String());
Sql("UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}
public override void Down()
{
DropColumn("dbo.Posts", "Abstract");
}
}
}
La migración editada tiene buen aspecto, así que vamos a usar Update-Database para actualizar la base de datos. Especificamos la marca –Verbose para poder ver el SQL que se ejecuta en la base de datos.
- Ejecute el comando Update-Database –Verbose en la consola del Administrador de paquetes.
Migrar a una versión específica (incluyendo degradación)
Hasta ahora siempre hemos actualizado a la migración más reciente, pero puede haber ocasiones en que quiera actualizar o degradar a una migración específica.
Supongamos que queremos migrar la base de datos al estado en que estaba después de ejecutar la migración AddBlogUrl. Podemos usar el modificador –TargetMigration para revertir a esta migración.
- Ejecute el comando Update-Database –TargetMigration: AddBlogUrl en la consola del Administrador de paquetes.
Este comando ejecutará el script Down para nuestras migraciones AddBlogAbstract y AddPostClass.
Si quiere revertir a una base de datos vacía, puede usar el comando Update-Database –TargetMigration: $InitialDatabase.
Obtención de un script SQL
Si otro desarrollador quiere estos cambios en su equipo, puede simplemente sincronizar una vez que registremos nuestros cambios en el control de código fuente. Cuando tenga las nuevas migraciones, puede ejecutar el comando Update-Database para que los cambios se apliquen localmente. Pero si queremos enviar estos cambios a un servidor de prueba y, finalmente, a producción, probablemente querremos un script SQL que podamos pasar al DBA.
- Ejecute el comando Update-Database, pero esta vez especifique la marca –Script para que los cambios se escriban en un script en lugar de aplicarse. Especificaremos una migración de origen y destino con el fin de generar el script. Queremos un script que abarque desde una base de datos vacía ($InitialDatabase) a la versión más reciente (migración AddPostAbstract). Si no se especifica una migración de destino, Migraciones usa la última migración como destino. Si no se especifica una migración de origen, Migraciones usa el estado actual de la base de datos.
- Ejecute el comando Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: AddPostAbstract en la consola del Administrador de paquetes
Migraciones de Code First ejecutará el proceso de migración, pero en lugar de aplicar los cambios, los escribirá en un archivo .sql para ti. Una vez que se ha generado el script, se abre automáticamente en Visual Studio, listo para verse o guardarse.
Generación de scripts idempotentes
A partir de EF6, si especifica –SourceMigration $InitialDatabase, el script generado será «idempotente». Los scripts idempotentes pueden actualizar una base de datos de cualquier versión a la versión más reciente (o la versión especificada si se usa –TargetMigration). El script generado incluye lógica para comprobar la tabla _MigrationsHistory y aplicar solo los cambios que no se han aplicado anteriormente.
Actualización automática al iniciar la aplicación (inicializador MigrateDatabaseToLatestVersion)
Si está desplegando su aplicación, puede querer que esta actualice la base de datos automáticamente (aplicando las migraciones pendientes) cuando se inicie la aplicación. Puede hacerlo si registra el inicializador de base de datos MigrateDatabaseToLatestVersion. Un inicializador de base de datos simplemente contiene alguna lógica que se usa para asegurarse de que la base de datos se ha configurado correctamente. Esta lógica se ejecuta la primera vez que se usa el contexto dentro del proceso de aplicación (AppDomain).
Se puede actualizar el archivo Program.cs, como se muestra a continuación, para establecer el inicializador MigrateDatabaseToLatestVersion para BlogContext antes de usar el contexto (línea 14). Tenga en cuenta que también debe agregar una declaración 'using' para el espacio de nombres System.Data.Entity (línea 5).
Cuando se crea una instancia de este inicializador, se debe especificar el tipo de contexto (BlogContext) y la configuración de las migraciones (Configuration): la configuración de las migraciones es la clase que se ha agregado a la carpeta Migraciones al habilitar Migraciones.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using MigrationsDemo.Migrations;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>());
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
Ahora, siempre que se ejecute la aplicación, en primer lugar comprobará si la base de datos de destino está actualizada y, si no lo está, aplicará las migraciones pendientes.