Aplicación de migraciones

Una vez agregadas las migraciones, deben implementarse y aplicarse a las bases de datos. Hay varias estrategias para hacerlo. Algunas son más adecuadas para entornos de producción, y otras, para el ciclo de vida de desarrollo.

Nota:

Independientemente de la estrategia de implementación, inspeccione siempre las migraciones generadas y pruébelas antes de aplicarlas a una base de datos de producción. Una migración puede quitar una columna cuando la intención era cambiarle el nombre, o puede producir un error por diversos motivos cuando se aplica a una base de datos.

Scripts de SQL

La manera recomendada de implementar migraciones en una base de datos de producción es mediante la generación de scripts SQL. Entre las ventajas de esta estrategia se incluyen las siguientes:

  • Los scripts SQL se pueden revisar para obtener precisión. Esto es importante, ya que aplicar cambios de esquema a las bases de datos de producción es una operación potencialmente peligrosa que podría implicar la pérdida de datos.
  • En algunos casos, los scripts se pueden ajustar para adaptarse a las necesidades específicas de una base de datos de producción.
  • Los scripts SQL se pueden usar junto con una tecnología de implementación e incluso se pueden generar como parte del proceso de CI.
  • Los scripts SQL se pueden proporcionar a un DBA y se pueden administrar y archivar por separado.

Uso básico

El siguiente código genera un script SQL de una base de datos en blanco a la migración más reciente:

dotnet ef migrations script

Con From (To implícito)

El siguiente código genera un script SQL de la migración especificada a la migración más reciente.

dotnet ef migrations script AddNewTables

Con From y To

El siguiente código genera un script SQL de la migración from especificada a la migración to especificada.

dotnet ef migrations script AddNewTables AddAuditTable

Puede usar un valor from que sea más reciente que el valor to para generar un script de reversión.

Advertencia

Tome nota de los posibles escenarios de pérdida de datos.

La generación de scripts acepta los dos argumentos siguientes para indicar qué intervalo de migraciones debe generarse:

  • La migración from debe ser la última migración aplicada a la base de datos antes de ejecutar el script. Si no se han aplicado migraciones, especifique 0 (es el valor predeterminado).
  • La migración to debe ser la última migración que se va a aplicar a la base de datos después de ejecutar el script. El valor predeterminado es la última migración del proyecto.

Scripts SQL idempotentes

Los scripts SQL generados en la sección anterior solo se pueden aplicar para cambiar el esquema de una migración a otra. Es su responsabilidad aplicar el script adecuadamente y solo a las bases de datos con el estado de migración correcto. EF Core también admite la generación de scripts idempotentes, que comprueban internamente qué migraciones se han aplicado ya (a través de la tabla del historial de migraciones) y solo aplican las que faltan. Esto es útil si no sabe exactamente cuál ha sido la última migración aplicada a la base de datos o si va a hacer una implementación en varias bases de datos que pueden estar en migraciones diferentes.

El siguiente código genera migraciones idempotentes:

dotnet ef migrations script --idempotent

Herramientas de línea de comandos

Las herramientas de línea de comandos de EF se pueden usar para aplicar migraciones a una base de datos. Aunque es productivo para el desarrollo y las pruebas de migraciones en modo local, este enfoque no es ideal para administrar bases de datos de producción:

  • La herramienta aplica directamente los comandos SQL, sin dar al desarrollador la oportunidad de inspeccionarlos o modificarlos. Esto puede ser peligroso en un entorno de producción.
  • El SDK de .NET y la herramienta EF deben instalarse en servidores de producción y esto requiere el código fuente del proyecto.

Nota:

Cada migración se aplica en su propia transacción. Vea Problema de GitHub nº 22616 para obtener una explicación de las posibles mejoras futuras en esta área.

El siguiente código actualiza la base de datos a la migración más reciente:

dotnet ef database update

El siguiente código actualiza la base de datos a una migración determinada:

dotnet ef database update AddNewTables

Tenga en cuenta que esto también se puede usar para revertir a una migración anterior.

Advertencia

Tome nota de los posibles escenarios de pérdida de datos.

Para obtener más información sobre cómo aplicar migraciones con las herramientas de línea de comandos, consulte la referencia de las herramientas de EF Core.

Agrupaciones

Una agrupación de migraciones es un archivo ejecutable que se puede usar para aplicar migraciones a una base de datos. Solucionan algunas de las deficiencias de los scripts SQL y las herramientas de línea de comandos:

  • La ejecución de scripts SQL requiere herramientas adicionales.
  • El control de transacciones y el comportamiento de continuar en caso de error de estas herramientas son incoherentes y a veces inesperados. Esto puede dejar una base de datos en un estado indefinido si se produce un error al aplicar migraciones.
  • Las agrupaciones se pueden generar como parte del proceso de CI y se pueden ejecutar fácilmente más adelante como parte del proceso de implementación.
  • Las agrupaciones se pueden ejecutar sin instalar el SDK de .NET ni la herramienta EF (o incluso el entorno de ejecución de .NET, cuando es independiente) y no requieren el código fuente del proyecto.

El siguiente código genera una agrupación:

dotnet ef migrations bundle

El siguiente código genera una agrupación independiente para Linux:

dotnet ef migrations bundle --self-contained -r linux-x64

Para obtener más información sobre la creación de agrupaciones, consulte la referencia de las herramientas de EF Core.

efbundle

El archivo ejecutable resultante se denomina efbundle de forma predeterminada. Se puede usar para actualizar la base de datos a la migración más reciente. Esto equivale a ejecutar dotnet ef database update o Update-Database.

Argumentos:

Argumento Descripción
<MIGRATION> Migración de destino. Si es "0", se revierten todas las migraciones. El valor predeterminado es la migración más reciente.

Opciones:

Opción Short Descripción
--connection <CONNECTION> La cadena de conexión a la base de datos. El valor predeterminado es el especificado en AddDbContext o OnConfiguring.
--verbose -v Mostrar resultado detallado.
--no-color No colorear la salida.
--prefix-output Agregar nivel como prefijo a la salida.

En el ejemplo siguiente se aplican migraciones a una instancia local de SQL Server usando el nombre de usuario y la contraseña especificados.

.\efbundle.exe --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;User ID=myUsername;Password=myPassword'

Advertencia

No olvide copiar appsettings.json junto con la agrupación. La agrupación se basa en la presencia de appsettings.json en el directorio de ejecución.

Ejemplo de agrupación de migraciones

Una agrupación necesita que se incluyan migraciones. Estas migraciones se crean usando dotnet ef migrations add como se explica en Creación de la primera migración. Una vez que tenga las migraciones listas para implementarse, cree una agrupación mediante dotnet ef migrations bundle. Por ejemplo:

PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations bundle
Build started...
Build succeeded.
Building bundle...
Done. Migrations Bundle: C:\local\AllTogetherNow\SixOh\efbundle.exe
PS C:\local\AllTogetherNow\SixOh>

La salida es un ejecutable adecuado para el sistema operativo de destino. En este caso, es Windows x64, por lo que aparece una instancia de efbundle.exe en la carpeta local. Al ejecutar este ejecutable se aplican las migraciones que contiene:

PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
Applying migration '20210903083845_MyMigration'.
Done.
PS C:\local\AllTogetherNow\SixOh>

Al igual que con dotnet ef database update o Update-Database, las migraciones se aplican a la base de datos solo si aún no se han aplicado. Por ejemplo, la ejecución de la misma agrupación de nuevo no hace nada, ya que no hay migraciones nuevas que aplicar:

PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
No migrations were applied. The database is already up to date.
Done.
PS C:\local\AllTogetherNow\SixOh>

Pero si se realizan cambios en el modelo y se generan más migraciones con dotnet ef migrations add, se pueden agrupar en un nuevo ejecutable listo para su aplicación. Por ejemplo:

PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations add SecondMigration
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations add Number3
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations bundle --force
Build started...
Build succeeded.
Building bundle...
Done. Migrations Bundle: C:\local\AllTogetherNow\SixOh\efbundle.exe
PS C:\local\AllTogetherNow\SixOh>

Sugerencia

Se puede usar la opción --force para sobrescribir la agrupación actual con una nueva.

La ejecución de esta nueva agrupación aplica estas dos migraciones nuevas a la base de datos:

PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
Applying migration '20210903084526_SecondMigration'.
Applying migration '20210903084538_Number3'.
Done.
PS C:\local\AllTogetherNow\SixOh>

De forma predeterminada, la agrupación usa la cadena de conexión de base de datos de la configuración de la aplicación. Pero se puede realizar la migración de otra base de datos si se pasa la cadena de conexión en la línea de comandos. Por ejemplo:

PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe --connection "Data Source=(LocalDb)\MSSQLLocalDB;Database=SixOhProduction"
Applying migration '20210903083845_MyMigration'.
Applying migration '20210903084526_SecondMigration'.
Applying migration '20210903084538_Number3'.
Done.
PS C:\local\AllTogetherNow\SixOh>

Nota:

Esta vez, se han aplicado las tres migraciones, ya que aún no se había aplicado ninguna de ellas a la base de datos de producción.


Aplicar migraciones en tiempo de ejecución

Es posible que la propia aplicación aplique migraciones mediante programación, normalmente durante el inicio. Aunque es productivo para el desarrollo y las pruebas de migraciones en modo local, este enfoque es inadecuado para administrar bases de datos de producción, por los siguientes motivos:

  • Si se están ejecutando dos instancias de la aplicación, ambas aplicaciones podrían intentar aplicar la migración simultáneamente y producir un error (o peor, dañar los datos).
  • De forma similar, si una aplicación accede a la base de datos mientras otra aplicación la migra, esto puede provocar problemas graves.
  • La aplicación debe tener acceso con privilegios elevados para modificar el esquema de la base de datos. En general, se recomienda limitar los permisos de las aplicaciones para las bases de datos en producción.
  • Es importante poder revertir una migración aplicada en el caso de que surja un problema. Las otras estrategias permiten hacer esto fácilmente y sin necesidad de configurar nada.
  • El programa aplica directamente los comandos SQL, sin dar al desarrollador la oportunidad de inspeccionarlos o modificarlos. Esto puede ser peligroso en un entorno de producción.

Para aplicar migraciones mediante programación, llame a context.Database.Migrate(). Por ejemplo, una aplicación de ASP.NET típica puede hacer lo siguiente:

public static void Main(string[] args)
{
    var host = CreateHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
        db.Database.Migrate();
    }

    host.Run();
}

Tenga en cuenta que Migrate() se basa en el servicio IMigrator, que se puede usar para escenarios más avanzados. Use myDbContext.GetInfrastructure().GetService<IMigrator>() para acceder a él.

Advertencia

  • Piénselo detenidamente antes de usar este enfoque en producción. La experiencia ha demostrado que la simplicidad de esta estrategia de implementación se ve eclipsada por los problemas que crea. En su lugar, considere la posibilidad de generar scripts SQL a partir de migraciones.
  • No llame a EnsureCreated() antes de Migrate(). EnsureCreated() omite las migraciones para crear el esquema, lo cual provoca un error de Migrate().