Administración de migraciones

A medida que cambia el modelo, las migraciones se agregan y quitan como parte del desarrollo normal y los archivos de migración se revisan en el control de código fuente del proyecto. Para administrar las migraciones, primero debe instalar las herramientas de línea de comandos de EF Core.

Tip

Si DbContext está en un ensamblado diferente al del proyecto de inicio, puede especificar de manera explícita los proyectos de destino e inicio tanto en las herramientas de la Consola del Administrador de paquetes como en las herramientas de la CLI de .NET.

Agregar una migración

Después de cambiar el modelo, puede agregar una migración para ese cambio:

dotnet ef migrations add AddBlogCreatedTimestamp

El nombre de la migración se puede usar como un mensaje de confirmación en un sistema de control de versiones. Por ejemplo, puede elegir un nombre como AddBlogCreatedTimestamp si el cambio es una nueva CreatedTimestamp propiedad en la entidad Blog.

Se agregan tres archivos al proyecto en el directorio Migraciones :

  • XXXXXXXXXXXXXX_AddBlogCreatedTimestamp.cs: el archivo de migraciones principal. Contiene las operaciones necesarias para aplicar la migración (en Up) y revertirla (en Down).
  • XXXXXXXXXXXXXX_AddBlogCreatedTimestamp.Designer.cs: el archivo de metadatos de migraciones. Contiene información utilizada por EF.
  • MyContextModelSnapshot.cs: una instantánea de tu modelo actual. Se usa para determinar qué ha cambiado al agregar la siguiente migración.

La marca de tiempo del nombre de archivo ayuda a mantenerlos ordenados cronológicamente para que pueda ver la progresión de los cambios.

Namespaces

Puede mover los archivos de Migraciones y cambiar su espacio de nombres manualmente cuando quiera. Las nuevas migraciones se crean como hermanas de la última migración. Como alternativa, puede especificar el directorio en tiempo de generación de la siguiente manera:

dotnet ef migrations add InitialCreate --output-dir Your/Directory

Note

También puede cambiar el espacio de nombres independientemente del directorio mediante --namespace.

Creación y aplicación de una migración en un paso

Note

Esta característica se agregó en EF Core 11.

El dotnet ef database update comando admite la creación y aplicación de una migración en un solo paso mediante la --add opción . Esto usa Roslyn para compilar la migración en tiempo de ejecución, lo que permite escenarios como .NET Aspire y aplicaciones en contenedor en las que la aplicación no se puede detener y volver a compilar:

dotnet ef database update InitialCreate --add

Se pueden usar las mismas opciones disponibles para dotnet ef migrations add :

dotnet ef database update AddProducts --add --output-dir Migrations/Products --namespace MyApp.Migrations

Este comando genera una nueva migración con el nombre especificado, la compila usando Roslyn y la aplica inmediatamente a la base de datos. Los archivos de migración todavía se guardan en el disco para el control de código fuente y la recompilación futura.

Si no se detectan cambios en el modelo pendientes, el comando aplica las migraciones pendientes existentes sin crear una nueva.

Personalización del código de migración

Aunque EF Core generalmente crea migraciones precisas, siempre debe revisar el código y asegurarse de que corresponde al cambio deseado; en algunos casos, incluso es necesario hacerlo.

Cambio de nombre de columna

Un ejemplo notable en el que se requieren migraciones de personalización es cuando se cambia el nombre de una propiedad. Por ejemplo, si cambia el nombre de una propiedad de Name a FullName, EF Core generará la siguiente migración:

migrationBuilder.DropColumn(
    name: "Name",
    table: "Customers");

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customers",
    nullable: true);

Por lo general, EF Core no puede saber cuándo la intención es quitar una columna y crear una nueva (dos cambios independientes) y cuándo se debe cambiar el nombre de una columna. Si se aplica la migración anterior tal cual, se perderán todos los nombres de los clientes. Para cambiar el nombre de una columna, reemplace la migración generada anteriormente por lo siguiente:

migrationBuilder.RenameColumn(
    name: "Name",
    table: "Customers",
    newName: "FullName");

Tip

El proceso de scaffolding de la migración advierte si una operación puede ocasionar una pérdida de datos (como el borrado de una columna). Si ve esa advertencia, asegúrese especialmente de revisar el código de migraciones para asegurar su precisión.

Operaciones de datos

Las migraciones pueden mover datos, así como cambiar el esquema. Elija la operación en función de si los valores se conocen cuando se escribe la migración:

  • Use InsertData, UpdateDatay DeleteData para valores fijos y filas identificados por claves explícitas. EF Core convierte estas operaciones en SQL específico del proveedor, por lo que también funcionan al generar scripts y agrupaciones.
  • Use Sql cuando los nuevos valores se deben calcular a partir de los datos de base de datos existentes. La sintaxis SQL puede diferir por proveedor; bifurcación en MigrationBuilder.ActiveProvider cuando sea necesario.
  • Defina una operación de migración personalizada cuando una operación reutilizable necesite la generación de SQL específica del proveedor.

No use los tipos CLR actuales DbContext o de entidad para mover datos en una migración. Las migraciones históricas deben seguir compilando y comportándose igual después de que esos tipos se cambien o quiten.

Transformación de datos existentes

Al reemplazar columnas, conserve los datos de origen hasta que se haya rellenado el destino:

  1. Agregue la columna de destino como que acepta valores NULL.
  2. Lléntelo a partir de las columnas existentes.
  3. Haga que la columna de destino sea necesaria, si procede.
  4. Quite las columnas de origen.

La siguiente migración implementa esa secuencia para SQL Server y SQLite:

migrationBuilder.AddColumn<string>(
    name: "FullName",
    table: "Customers",
    nullable: true);

if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
{
    migrationBuilder.Sql(
        """
        UPDATE [Customers]
        SET [FullName] = [FirstName] + N' ' + [LastName];
        """);
}
else if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
{
    migrationBuilder.Sql(
        """
        UPDATE "Customers"
        SET "FullName" = "FirstName" || ' ' || "LastName";
        """);
}
else
{
    throw new NotSupportedException(
        $"Data migration is not implemented for provider {migrationBuilder.ActiveProvider}.");
}

migrationBuilder.AlterColumn<string>(
    name: "FullName",
    table: "Customers",
    nullable: false,
    oldClrType: typeof(string),
    oldNullable: true);

migrationBuilder.DropColumn(
    name: "FirstName",
    table: "Customers");

migrationBuilder.DropColumn(
    name: "LastName",
    table: "Customers");

Agregue una rama para cada proveedor que admita la aplicación. Iniciar para un proveedor desconocido es más seguro que aplicar silenciosamente una migración incompleta. No compile SQL a partir de valores que no son de confianza; migration SQL se ejecuta con privilegios de cambio de esquema.

Algunas transformaciones no se pueden invertir sin perder información. Implemente Down solo cuando los valores originales se puedan reconstruir de forma segura. De lo contrario, produzca un error explícitamente y requiera restaurar los datos de una copia de seguridad como parte del procedimiento de reversión.

Insertar datos fijos

Use InsertData cuando se conozcan las claves y los valores cuando se escriba la migración:

migrationBuilder.InsertData(
    table: "Countries",
    columns: new[] { "CountryId", "Name" },
    values: new object[,]
    {
        { 1, "United States" },
        { 2, "Canada" }
    });

El método correspondiente Down debe llamar a DeleteData con las mismas claves.

Actualización de datos fijos

UpdateData identifica una fila por su clave y establece una o varias columnas en valores fijos:

migrationBuilder.UpdateData(
    table: "Countries",
    keyColumn: "CountryId",
    keyValue: 1,
    column: "Name",
    value: "United States of America");

El Down método debe restaurar los valores anteriores.

Eliminación de datos fijos

DeleteData también identifica las filas por clave:

migrationBuilder.DeleteData(
    table: "Countries",
    keyColumn: "CountryId",
    keyValue: 2);

Si la eliminación debe ser reversible, el Down método debe usar InsertData para restaurar todos los valores eliminados. Estas operaciones no consultan el estado actual de la base de datos; usar Sql o inicializar la propagación en tiempo de inicialización cuando el comportamiento depende de los datos existentes.

Cambios arbitrarios usando SQL en bruto

SQL sin procesar también se puede usar para administrar objetos de base de datos que EF Core no conoce. Para ello, agregue una migración sin realizar ningún cambio de modelo; Se generará una migración vacía, que después puede rellenar con operaciones SQL sin procesar.

Por ejemplo, la migración siguiente crea un procedimiento almacenado de SQL Server:

migrationBuilder.Sql(
@"
    EXEC ('CREATE PROCEDURE getFullName
        @LastName nvarchar(50),
        @FirstName nvarchar(50)
    AS
        SELECT @LastName + @FirstName;')");

Tip

EXEC se usa cuando una instrucción debe ser la primera o solo una en un lote de SQL. También se puede usar para solucionar errores del analizador en scripts de migración idempotentes que pueden producirse cuando las columnas a las que se hace referencia no existen actualmente en una tabla.

Esto se puede usar para administrar cualquier aspecto de la base de datos, entre los que se incluyen:

  • Procedimientos almacenados
  • Búsqueda de texto completo
  • Functions
  • Triggers
  • Views

En la mayoría de los casos, EF Core ajustará automáticamente cada migración en su propia transacción al aplicar las migraciones. Desafortunadamente, algunas operaciones de migración no se pueden realizar dentro de una transacción en algunas bases de datos; en estos casos, puede no participar en la transacción pasando suppressTransaction: true a migrationBuilder.Sql.

Note

En EF Core 9, EF Core abarca todas las migraciones pendientes con una sola transacción de forma predeterminada (esto se revierte en EF Core 10). Consulte la nota de cambio importante para obtener más información.

Quitar una migración

A veces, agrega una migración y se da cuenta de que necesita realizar cambios adicionales en el modelo de EF Core antes de aplicarla. Para quitar la última migración, use este comando.

dotnet ef migrations remove

Después de quitar la migración, puede realizar los cambios adicionales del modelo y agregarlo de nuevo.

Warning

Evite quitar las migraciones que ya se han aplicado a las bases de datos de producción. Esto significa que no podrá revertir esas migraciones en las bases de datos y puede romper las suposiciones realizadas por las migraciones posteriores.

Si la migración se aplicó localmente

Para una base de datos de desarrollo descartable, actualice primero la base de datos a la migración anterior y, a continuación, quite la migración del proyecto. Use 0 como destino al quitar la primera migración.

dotnet ef database update PreviousMigration
dotnet ef migrations remove

Como alternativa, --force realiza ambos pasos:

dotnet ef migrations remove --force

Si la migración se aplicó a una base de datos compartida

No elimine una migración que se haya aplicado a una base de datos compartida, de prueba o de producción. Normalmente, mantenga la migración en el proyecto y agregue una nueva migración correctiva. Si se requiere una reversión planeada, ejecute la reversión mientras el código de migración original sigue estando disponible y coordina la implementación de la aplicación y la base de datos.

Eliminación de una migración no aplicación anterior

Las herramientas quitan solo la migración más reciente. No elimine una migración desde el centro de la secuencia y edite manualmente la instantánea del modelo. Si la migración y cada migración después de que no se publica y no se aplica, quite las migraciones posteriores en orden inverso, quite la migración no deseada y, a continuación, vuelva a aplicar scaffolding al modelo retenido.

Si las migraciones se crearon en distintas ramas, siga el flujo de trabajo del árbol de migración diverged en su lugar.

Enumeración de migraciones

Puede enumerar todas las migraciones existentes de la siguiente manera:

dotnet ef migrations list

También puede inspeccionar el estado de migración mediante programación:

var allMigrations = context.Database.GetMigrations();
var appliedMigrations = await context.Database.GetAppliedMigrationsAsync();
var pendingMigrations = await context.Database.GetPendingMigrationsAsync();

GetPendingMigrationsAsync compara las migraciones en el ensamblado de migraciones configuradas con las migraciones registradas en la base de datos de destino. No detecta los cambios del modelo que no se han capturado en una migración; use la comprobación de cambios del modelo pendiente a continuación para ello.

Comprobando los cambios de modelo pendientes

Note

Esta característica se agregó en EF Core 8.0.

A veces, es posible que desee comprobar si se han realizado cambios en el modelo desde la última migración. Esto puede ayudarle a saber cuándo usted o un compañero de equipo han olvidado agregar una migración. Una manera de hacerlo es usar este comando.

dotnet ef migrations has-pending-model-changes

También puede realizar esta comprobación mediante programación mediante context.Database.HasPendingModelChanges(). Esto se puede usar para escribir una prueba unitaria que produce un error cuando se olvida de agregar una migración.

Note

A partir de la versión 9 de EF Core, llamar a Migrate o MigrateAsync con cambios pendientes en el modelo genera una excepción (identificador de evento PendingModelChangesWarning). Consulte la documentación sobre la aplicación de migraciones y la nota sobre cambios incompatibles para obtener más información.

Restablecer todas las migraciones

En algunos casos extremos, puede ser necesario quitar todas las migraciones y empezar de nuevo. Esto se puede hacer fácilmente mediante la eliminación de la carpeta Migraciones y la eliminación de la base de datos; en ese momento puede crear una nueva migración inicial, que contendrá todo el esquema actual.

También es posible restablecer todas las migraciones y crear una única sin perder los datos. Esto se denomina migración de aplastamiento e implica un trabajo manual. EF Core no proporciona actualmente un comando de squash automatizado; consulte dotnet/efcore#2174.

  1. Haga una copia de seguridad de la base de datos, en caso de que algo se produzca un error.
  2. En la base de datos, elimine todas las filas de la tabla del historial de migraciones (por ejemplo, DELETE FROM [__EFMigrationsHistory] en SQL Server).
  3. Elimine la carpeta Migraciones .
  4. Cree una nueva migración y genere un script SQL para él (dotnet ef migrations script).
  5. Inserte una sola fila en el historial de migraciones para registrar que ya se ha aplicado la primera migración, ya que las tablas ya están allí. Insert SQL es la última operación del script SQL generado anteriormente y es similar a la siguiente (no olvide actualizar los valores):
INSERT INTO [__EFMigrationsHistory] ([MIGRATIONID], [PRODUCTVERSION])
VALUES (N'<full_migration_timestamp_and_name>', N'<EF_version>');

Warning

Cualquier código de migración personalizado se perderá cuando se elimine la carpeta Migraciones . Las personalizaciones deben aplicarse manualmente a la nueva migración inicial para conservarse.

Antes de aplastar, compruebe que todas las bases de datos implementadas están en una migración conocida y realice una copia de seguridad. Las nuevas bases de datos deben crearse a partir de la nueva migración inicial, mientras que las bases de datos existentes deben tener registrada la migración de reemplazo sin ejecutar operaciones de esquema que ya se hayan aplicado. Pruebe ambas rutas de acceso antes de la implementación.

Recursos adicionales