lunes, 23 de abril de 2012

Entity Framework Code First DbMigration (I)

Siguiendo con mi línea de aprendizaje de Entity Framework esta vez le ha tocado el turno a una de las novedades introducidas en la versión 4.3, las migraciones automáticas o DbMigration. Creo que su nombre deja claro para que sirve, pero básicamente nos da soporte para realizar actualizaciones de nuestra base de datos. ¿Y qué podemos actualizar?, pues básicamente podremos crear, renombrar y borrar tanto tablas como columnas, así como crear índices, claves ajenas o incluso ejecutar sentencias SQL para actualizar registros. ¿Impresionado? ¿No? Pues deberías, y sino sigue leyendo para que veas lo que podrás hacer.

Bien, para poder hacer uso de las migraciones deberás tener instalado la versión 4.3 de Entity Framework como mínimo, en caso que no tengas estas versión o no estés seguro de que versión estás usando te recomiendo una lectura de mi artículo sobre Entity Framework Fluent Api, donde en la primera parte cuento como actualizar el Entity Framework a través del Package Manager Console de Visual Studio.

Lo primero que haremos será crear nuestras clases POCO, y nuestro DataContext (esto también está explicado en el artículo anteriormente mencionado).
Primero las clases POCO
public class Blog
{
 public int IdBlog { get; set; }
 public string Title { get; set; }
 public string Url { get; set; }

 // Navigation properties
 public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
 public int IdPost { get; set; }
 public string Title { get; set; }
 public string Body { get; set; }  

 // Foreign key
 public int IdBlog { get; set; }

 // Navigation properties
 public virtual Blog Blog { get; set; }
 public virtual Category Category { get; set; }
 public virtual ICollection<Tag> Tags { get; set; }
}

public class Category
{
 public int IdCategory { get; set; }
 public string Name { get; set; }

 // Navigation properties
 public virtual ICollection<Post> Posts { get; set; }
}

public class Tag
{
 public int IdTag { get; set; }
 public string Name { get; set; }

 // Navigation properties
 public virtual ICollection<Post> Posts { get; set; }
}

Y ahora nuestro DbContext
public class DataContext : DbContext
{
 public DataContext() : base("DataContext") {
            
 }

 public DataContext(string nameOrConnectionString) : base(nameOrConnectionString)
 {

 }

 public DbSet<Blog> Blog { get; set; }
 public DbSet<Post> Post { get; set; }
 public DbSet<Category> Category { get; set; }
 public DbSet<Tag> Tag { get; set; }

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
 {
  modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
  
  // Primary Keys
  modelBuilder.Entity<Blog>().HasKey(c => c.IdBlog);
  modelBuilder.Entity<Post>().HasKey(c => c.IdPost);
  modelBuilder.Entity<Category>().HasKey(c => c.IdCategory);
  modelBuilder.Entity<Tag>().HasKey(c => c.IdTag);

  // Properties
  modelBuilder.Entity<Blog>().Property(p => p.Title).IsRequired();
  modelBuilder.Entity<Blog>().Property(p => p.Title).HasMaxLength(50);

  modelBuilder.Entity<Post>().Property(p => p.Title).IsRequired();
  modelBuilder.Entity<Post>().Property(p => p.Title).HasMaxLength(50);
 }
}
Llegados a este punto ya podemos empezar a usar las migraciones propiamente dichas. Lo primero que haremos será habilitar las migraciones ejecutando lo siguiente en la consola
PM> Enable-Migrations
Si todo va bien deberemos ver algo parecido a esto
Detected database created with a database initializer. Scaffolded migration '201204191333459_InitialCreate' corresponding to current database schema. To use an automatic migration instead, delete the Migrations folder and re-run Enable-Migrations specifying the -EnableAutomaticMigrations parameter.
Code First Migrations enabled for project EntityFramework.CodeFirst.DbMigration.
¿Qué mas ha pasado? Pues se habrá creado una carpeta en nuestro proyecto con dos ficheros
  • *_InitialCreate.cs: Donde tendremos el script de creación de nuestra base de datos, en base a nuestro modelo de clase POCO.
  • Configuration.cs: Donde podremos configurar la migración y sobreescribiendo el método Seed llenar de datos nuestra base de datos.
¿Y ahora? Pues simple, actualizaremos nuestra base de datos con el script que hemos creado. Para esto usaremos el siguiente comando
PM> Update-Database
Y si todo ha ido bien veremos algo así
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201204191333459_InitialCreate].
Applying explicit migration: 201204191333459_InitialCreate.
Si vamos a la base de datos veremos que se ha creado la base de datos y dentro de la tablas del sistema se ha creado __MigrationHistory. Esta tabla es usada por el Entity Framework para llevar un control de las versiones.

Ahora supongamos que queremos añadir un campo a nuestra tabla blog, por ejemplo el campo Author, que deberá ser de tipo varchar, obligatorio y tener una longitud máxima de 250 caracteres. Lo primero será añadir este campo al objeto Blog como propiedad
public string Author { get; set; }
Lo siguiente será crear un nuevo script de migración de la siguiente forma
PM> Add-Migration BlogAddAuthor
Y si todo va bien veremos lo siguiente
Scaffolding migration 'BlogAddAuthor'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration 201204191351431_BlogAddAuthor' again.
También veremos que se ha creado un fichero *_BlogAddAuthor.cs, con esta estructura
public partial class BlogAddAuthor : DbMigration
{
    public override void Up()
    {
        AddColumn("Blog", "Author", c => c.String());
    }
        
    public override void Down()
    {
        DropColumn("Blog", "Author");
    }
}
Primero decir, que el método Up se llama cuando actualizamos (update) la base de datos y el método Down se llama cuando retrocedemos a una versión (downgrade). Aunque podemos modificar esto para ajustarnos a nuestras necesidades
public override void Up()
    {
        AddColumn("Blog", "Author", c => c.String(nullable: false, maxLength: 250));
    }
¿Qué cosas podemos hacer en estos métodos? Pues tenemos a nuestro alcance los siguiente métodos a través de la clase DbMigration
  • AddColumn
  • AddForeignKey
  • AlterColumn
  • CreateIndex
  • CreateTable
  • DropColumn
  • DropForeignKey
  • DropIndex
  • DropPrimaryKey
  • DropTable
  • MoveTable
  • RenameColumn
  • RenameTable Sql
Si no hubiéramos añadido la propiedad a la clase, obtendríamos este fichero vacio y tendríamos que añadir las sentencias AddColumn y DropColumn nosotros mismos (no siempre todos lo campos de la base de datos estarán en nuestras clases). Para actualizar nuestro modelo nuevamente usaremos Update-Database aunque esta vez usaremos el modificador -Verbose para ver un log de lo se está haciendo
PM> Update-Database -Verbose
Using NuGet project 'EntityFramework.CodeFirst.DbMigration'.
Using StartUp project 'EntityFramework.CodeFirst.DbMigration'.
Target database is: 'DbMigration' (DataSource: .\sql2k8r2, Provider: System.Data.SqlClient, Origin: Configuration).
Applying explicit migrations: [201204191358443_BlogAddAuthor].
Applying explicit migration: 201204191358443_BlogAddAuthor.
ALTER TABLE [Blog] ADD [Author] [nvarchar](250) NOT NULL DEFAULT ''
[Inserting migration history record]


¿Podemos hacer más? Pues si, por ejemplo volver a un versión. En este caso volveremos a la versión inicial ejecutando esto
PM> Update-Database –TargetMigration:$InitialDatabase –Force
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Reverting migrations: [201204191358443_BlogAddAuthor, 201204191333459_InitialCreate].
Reverting explicit migration: 201204191358443_BlogAddAuthor.
Reverting explicit migration: 201204191333459_InitialCreate.
Si queremos ir a nuestra versión en concreto ejecutaremos
‘Update-Database –TargetMigration:"NombreDeLaVersion" –Force’
Para obtener más ayuda sobre los distintos comandos que tenemos (Enable-Migrations: Enables Code First Migrations in a project. Add-Migration: Scaffolds a migration script for any pending model changes. Update-Database: Applies any pending migrations to the database. Get-Migrations) tan solo debemos escribir lo siguiente en la Package Manager Console
PM> get-help Update-Database
Y obtendremos una ayuda detallada del comando.

Como vemos tenemos en nuestras manos una excelente herramienta para llevar una sincronización perfecta de nuestras base de datos

Happy coding!

No hay comentarios:

Publicar un comentario