En el post anterior vimos que es code first en entity framework core, pero en ese post, solo vimos la punta del iceberg, una pequeña explicación al funcionamiento del mismo. En este post, vamos a ver como el 99.9% de las empresas implementan code first. Utilizando migrations.
Índice
1 - Qué son las migraciones en Entity framework Core?
Las migraciones o migrations en Entity Framework core son la forma de mantener la estructura del modelo que estás utilizando en tu aplicación sincronizada con la base de datos. En otras palabras, te permiten ir actualizando tu código y que la base de datos se vaya actualizando acordemente.
Y esto Entity Framework lo permite hacer de una forma muy sencilla.
Para este post, vamos a utilizar el código que está en GitHub que ya vimos en el post anterior.
2 - Implementar migraciones en Entity Framework Core
como en otros posts vamos a empezar de cero. Lo único que tenemos por ahora son las entidades así como el DbContex
que como sabes representan la estrucura de nuestra base de datos:
public class Wokringexperience
{
public int Id { get; set; }
public int UserId { get; set; }
[MaxLength(50)]
public string Name { get; set; }
public string Details { get; set; }
public string Environment { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
}
ublic class CursoEfContext : DbContext
{
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<Wokringexperience> Wokringexperiences { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseMySQL("server=127.0.0.1;port=4306;database=cursoEF;user=root;password=cursoEFpass");
}
Ahora lo que vamos a hacer es generar las migraciones, para ello tenemos dos opciones.
A - Desde la línea de comandos, nos ubicamos en la carpeta que contiene el csproj
del proyecto que contiene el DbContext
y ejecutamos el siguiente código:
dotnet ef migrations add MigracionInicial
B - en la consola de nuget (package manager console) ejecutamos el siguiente comando:
add-migration MigracionInicial
Ambas opciones nos van a dar el mismo resultado, el cual consiste en los siguientes 3 ficheros:
1 - El primero es el fichero principal de la migración y contiene las operaciones de Up
y Down
. y si lo abrimos, podemos ver cómo Up
contiene toda la información de crear la base de datos mientras que Down
contiene la información para destruir lo creado con Up
.
public partial class MigracionInicial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterDatabase()
.Annotation("MySQL:Charset", "utf8mb4");
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
UserName = table.Column<string>(type: "longtext", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
})
.Annotation("MySQL:Charset", "utf8mb4");
migrationBuilder.CreateTable(
name: "Wokringexperiences",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
Name = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: false),
Details = table.Column<string>(type: "longtext", nullable: false),
Environment = table.Column<string>(type: "longtext", nullable: false),
StartDate = table.Column<DateTime>(type: "datetime(6)", nullable: true),
EndDate = table.Column<DateTime>(type: "datetime(6)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Wokringexperiences", x => x.Id);
})
.Annotation("MySQL:Charset", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "Wokringexperiences");
}
}
Esto se hace, principalmente, por si algo sale mal, para que podamos hacer un rollback de una forma sencilla.
2 - El fichero designer contiene los metadatos utilizados por Entity Framework Core.
3 - El fichero Snapshot es una “imagen” del modelo actual, es el fichero que utiliza Entity Framework Core, para detectar qué ha cambiado desde la última migración.
2.1 - Ejecutar migraciones en Entity Framework Core
Antes de continuar, asegúrate de que tienes tu base de datos corriendo y la conexión string es correcta, en mi caso estoy utilizando MySql.
Como mencioné en el post anterior, esta acción se solía hacer por línea de comandos, pero hoy en día se puede hacer en el código. La cual es asegurarse de que la base de datos se sincroniza con la migración que tienes en el código.
En mi caso no tengo base de datos creada, por lo que la migración la creará, además de crear las tablas.
vamos al código y en nuestro startup.cs
nos aseguramos de que el siguiente código esta presente para que la migración sea ejecutada
using (var scope = app.Services.CreateScope())
{
CursoEfContext context = scope.ServiceProvider.GetRequiredService<CursoEfContext>();
context.Database.Migrate();
}
Y ya está, simplemente ejecutamos el proyecto.
Esta acción nos ha creado ambas tablas y además una tabla adicional que contiene la información de las migraciones que han sido ejecutadas, para así saber en qué ‘versión’ de la migración está tu base de datos.
3 - Actualizar una base de datos con migrations en Entity Framework Core
Migrations está dentro de code first, lo que quiere decir que si tenemos que actualizar la base de datos, lo haremos a través de las propias migraciones.
Para nuestro escenario, vamos a añadir un nuevo campo dentro de la tabla Users
, en este caso el email
. Lo primero que tenemos que hacer es ir a nuestra entidad y añadir dicha propiedad:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
[MaxLength(50)]
public string Email { get; set; }
}
Y ahora debemos crear una nueva migración, intenta siempre darle un nombre que tenga sentido, que eso siempre ayuda.
dotnet ef migrations add AddEmailtoUser
Este comando nos va a crear el nuevo fichero de migración con su respectivo Up
and Down
.
public partial class AddEmailtoUser : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Email",
table: "Users",
type: "varchar(50)",
maxLength: 50,
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Email",
table: "Users");
}
}
Así como la actualización automática del Snapshot.
Ahora, cuando ejecutemos el código, Entity Framework se encarga de actualizar todo lo que tenga que actualizar para que tu base de datos encaje con el modelo en tú código.
En este caso simplemente incluye la nueva columna y además incluye el nuevo registro en la tabla de migraciones. Este es el resultado después de ejecutar la aplicación:
Cómo vemos es lo esperado.
4 - Revertir una migración en Entity Framework core
Qué sucece, si, durante la migración la hemos liado o la aplicación no esta bien testeada por lo que sea y hay que volver a la versión antigua, pero la base de datos esta a medias entre estar completo y la versión anterior? que nos toca deshacer la migración.
Nota: Para este escenario solo nos queda ejecutarla desde la línea de comandos o del package manager. Los comandos son los siguientes.
1 - Línea de comandos:
dotnet ef database update <NombreVersionAnteior>
2 - Package manager
Update-Database <NombreVersionAnteior>
Básicamente en NombreVersionAnteior
debemos poner el nombre de la migración, en nuestro caso vamos a deshacer la última migración, para ello ejecutamos el comando
dotnet ef database update MigracionInicial
y podemos ver cómo nos devuelve el aviso de que ha revertido la migración anterior
Y en la base de datos por supuesto no queda rastro, por lo que podríamos volver tranquilamente a desplegar la versión anterior del código.
- NOTA: Si por algún motivo, quitas el código de la parte de
Down
en la migración, revertirla no causaría ningún efecto.
4.1 - Eliminar una migración
En caso de que esa migración que has creado fuera errónea, no nos sirve con simplemente borrarla del código, ya que el fichero snapshot está modificado.
Lo que tendríamos que hacer es eliminarla con el comando dotnet ef migrations remove
, este comando elimina la última migración y pone el snapshot a como estaba anteriormente
conclusión.
En resumen, las migraciones son una herramienta muy útil para mantener tu base de datos sincronizada con el modelo de tu aplicación.
Con Entity Framework Core, puedes generar migraciones de manera automática y aplicarlas fácilmente a la base de datos con code first.