Correr MigrateAsync() ¿Estás dando derechos de propietario de tu base de datos de aplicaciones y esperando que nada salga mal? Hay una mejor manera: los paquetes de migración de EF te permiten ejecutar migraciones como un paso de CI controlado, manteniendo segura tu aplicación de producción. Pero esto es lo siguiente: a veces la manera "equivocada" está realmente bien. Vamos a explorar cuándo usar cada enfoque.
Documentos oficiales: Panorama general de las migraciones | Aplicación de las migraciones | Paquetes
Este blog utiliza MigrateAsync() En el inicio - el enfoque que estoy a punto de decirte que no uses. He aquí por qué eso está bien para mí, y por qué probablemente no es para ti.
En mi Program.cs Tengo lo siguiente:
using (var scope = app.Services.CreateScope())
{
var blogContext = scope.ServiceProvider.GetRequiredService<IMostlylucidDBContext>();
await blogContext.Database.MigrateAsync();
}
MigrateAsync() aplica las migraciones pendientes y crea la base de datos si es necesario. Simple - pero problemático:
db_owner Acabas de darle a tu aplicación de tiempo de ejecución las llaves para soltar las mesas.Por qué me salgo con la mía: datos públicos, una sola red Docker, proyecto personal. Probablemente no puedas.
Un paquete de EF es un ejecutable autónomo que contiene sus migraciones compiladas. dotnet ef database update empaquetado en una unidad independiente .exe.
¿Por qué ganan los paquetes?
db_owner; sólo el corredor CI lo hace, sólo durante el despliegueNota: Para la seguridad de la calidad de producción, utilizar Identidad administrada Pero los paquetes siguen siendo un gran paso adelante de las migraciones en tiempo de ejecución.
- name: Install EF Core tools
run: dotnet tool install --global dotnet-ef
- name: Add EF tools to PATH
run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH
- name: Generate EF migration bundle
run: |
dotnet ef migrations bundle \
--project ${{ env.WEB_PROJECT }} \
--output efbundle.exe \
--configuration ${{ env.BUILD_CONFIGURATION }} \
--runtime ${{ env.RUNTIME_IDENTIFIER }} \
--context AdminDbContext \
env:
AdminSite__ConnectionString: ${{ secrets.PROD_SQL_CONNECTIONSTRING }}
- name: Run EF migration bundle
run: |
./efbundle.exe
env:
AdminSite__ConnectionString: ${{ secrets.PROD_SQL_CONNECTIONSTRING }}
El paquete lee cadenas de conexión de variables de entorno y aplica migraciones pendientes. ¿Ya se ha aplicado? Sólo sale con éxito.
¿Quieres probar antes de empujar? Construye paquetes localmente.
Casos de uso: Prueba antes de CI, entrega DBA (exe autónomo, no necesita SDK), despliegues de puesta en escena, depuración con --verbose.
# Install EF CLI (once)
dotnet tool install --global dotnet-ef
# Basic bundle
dotnet ef migrations bundle \
--project Mostlylucid.DbContext \
--startup-project Mostlylucid \
--output efbundle.exe
# Self-contained (includes runtime - portable to machines without .NET)
dotnet ef migrations bundle \
--project Mostlylucid.DbContext \
--startup-project Mostlylucid \
--output efbundle.exe \
--self-contained
# Cross-platform (e.g., build on Windows, deploy to Linux)
dotnet ef migrations bundle \
--project Mostlylucid.DbContext \
--startup-project Mostlylucid \
--output efbundle \
--runtime linux-x64
# Using default connection string from appsettings.json
./efbundle.exe
# Override with a specific connection string
./efbundle.exe --connection "Host=localhost;Database=mostlylucid;Username=postgres;Password=secret"
# Using an environment variable (matches your config key)
$env:ConnectionStrings__DefaultConnection="Host=localhost;..." # PowerShell
export ConnectionStrings__DefaultConnection="Host=localhost;..." # Bash
./efbundle.exe
# See what migrations would be applied without running them
./efbundle.exe --dry-run
# Verbose output for debugging
./efbundle.exe --verbose
# Apply migrations up to a specific migration (useful for testing)
./efbundle.exe --target-migration "20231115_AddUserTable"
# Combine options
./efbundle.exe --verbose --dry-run
# 1. Create migration
dotnet ef migrations add AddNewFeature \
--project Mostlylucid.DbContext \
--startup-project Mostlylucid
# 2. Build bundle
dotnet ef migrations bundle \
--project Mostlylucid.DbContext \
--startup-project Mostlylucid \
--output efbundle.exe
# 3. Dry run first
./efbundle.exe --dry-run --verbose
# 4. Run for real
./efbundle.exe --verbose
# 5. Broken? Remove and retry
dotnet ef migrations remove \
--project Mostlylucid.DbContext \
--startup-project Mostlylucid
Captura errores de sintaxis, violaciones de restricciones, problemas FK - todos antes CI o producción.
La generación de paquetes es lenta - 30+ segundos en grandes proyectos. No generar en cada construcción.
Si realmente quieres auto-generación, agrega un objetivo de MSBuild:
<Target Name="BuildMigrationBundle">
<Exec Command="dotnet ef migrations bundle --output $(OutputPath)efbundle.exe --force" />
</Target>
Entonces: dotnet build -t:BuildMigrationBundle
Lo mejor de ambos mundos: comodidad local, seguridad en la producción.
if (builder.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<IMostlylucidDBContext>();
await context.Database.MigrateAsync();
}
// Production: CI pipeline runs the bundle
Generar SQL plano en lugar de un ejecutable. Ideal para la revisión de DBA y los procesos de gestión de cambios existentes.
# All migrations
dotnet ef migrations script --output migrations.sql
# Idempotent (safe to run multiple times) - USE THIS
dotnet ef migrations script --idempotent --output migrations.sql
# Range of migrations
dotnet ef migrations script FromMigration ToMigration --output migrations.sql
Pros: Visibilidad completa, cualquier cliente SQL puede ejecutarlo, control de versiones amigable, flujos de trabajo de aprobación DBA.
Contras: Sin seguimiento automático (utilizar --idempotent), ejecución manual, deriva potencial si los scripts se modifican.
Ver Documentos oficiales en scripts SQL.
- name: Generate and apply migrations
run: |
dotnet ef migrations script --idempotent --output migrations.sql
# SQL Server
sqlcmd -S ${{ secrets.DB_SERVER }} -d ${{ secrets.DB_NAME }} -i migrations.sql
# Or PostgreSQL
PGPASSWORD=${{ secrets.DB_PASSWORD }} psql -h ${{ secrets.DB_HOST }} -f migrations.sql
CADPACs son con sede en el Estado no sobre la base de la migración. Usted define el esquema deseado, y SqlPackage lo diferencia contra la base de datos de destino.
SqlPackage.exe /Action:Publish /SourceFile:MyDatabase.dacpac /TargetConnectionString:"..."
Pros: Schema como código, generación de auto-diff, maneja todo (tablas, vistas, SPs, índices), herramientas empresariales.
Contras: SQL Server solamente, esquema en dos lugares (modelos EF + proyecto SQL), el motor diff hace opciones cuestionables, los renombres de columna se ven como drop+add.
Ver SqlPackage docs.
Aproximación Mejor para Requiere .NET Auto-pistas Aplicadas DBA Friendly Cross-plataform DB
|----------|----------|---------------|---------------------|--------------|-------------------|
| MigrateAsync() # Dev/pequeños proyectos # # Sí (tiempo de ejecución) # # Sí # No # # Sí #
EF Bundles CI/CD tuberías No (self-contained) Sí Algo Sí
SQL Scripts ambientes controlados por DBA No Con --idempotent # Sí # # Sí # # Sí # # Sí # Sí # # Sí # Sí # # Sí # Sí # # Sí # # Sí # # Sí # Sí # # Sí # Sí # Sí # Sí # Sí # Sí #
CADPAC SQL Empresa de servidores No Sí (basado en el estado) Sí No
¿Las migraciones trabajan localmente pero no en CI? Compruebe que ha comprometido ambos archivos:
20231115_AddUserTable.cs - Código de migración20231115_AddUserTable.Designer.cs - La instantánea del modeloFalta el archivo Designer = fallo silencioso.
dotnet ef migrations bundle --context BlogDbContext --output blog-migrations.exe
dotnet ef migrations bundle --context IdentityDbContext --output identity-migrations.exe
--connection argumentoappsettings.jsonUsar variables de entorno en IC.
Las herramientas de EF necesitan instanciar su DbContext. Si su DbContext está en un proyecto separado o tiene un inicio complejo, implemente IDesignTimeDbContextFactory<T>:
public class AdminDbContextFactory : IDesignTimeDbContextFactory<AdminDbContext>
{
public AdminDbContext CreateDbContext(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true)
.AddEnvironmentVariables()
.AddUserSecrets<AdminDbContextFactory>()
.Build();
var connectionString = config["AdminSite:ConnectionString"]
?? throw new InvalidOperationException("Missing connection string");
var optionsBuilder = new DbContextOptionsBuilder<AdminDbContext>();
optionsBuilder.UseSqlServer(connectionString, sql => sql.CommandTimeout(120));
return new AdminDbContext(optionsBuilder.Options);
}
}
Utilice cuando: DbContext en un proyecto separado, inicio complejo, necesita secretos de usuario para el tiempo de diseño.
Preguntas comunes y retroceso que he recibido.
dotnet ef database update en CI?"Cubierto arriba, pero la versión corta: los paquetes son artefactos portátiles. Su paso de implementación no necesita EF CLI, código fuente o resolución de tiempo de diseño. El mismo paquete se ejecuta en pruebas, estadificación y prod - deriva cero.
Si estás solo, los datos son públicos, y el radio de explosión es bajo. MigrateAsync() Pero en el momento en que se agrega un segundo desarrollador, datos sensibles, o múltiples entornos, los paquetes pagan por sí mismos.
EF no hace retrocesos automáticos. Opciones:
Down() migración y ejecutarlo (pero tienes que haberlo escrito)Para sistemas críticos: probar primero las migraciones contra un clon de base de datos.
Sí. Bundle + init contenedor es un patrón sólido:
initContainers:
- name: migrate
image: myapp:latest
command: ["./efbundle.exe"]
env:
- name: ConnectionStrings__Default
valueFrom:
secretKeyRef:
name: db-secrets
key: connection-string
El contenedor de aplicaciones espera a que se complete el init.
Funcionan muy bien. Los paquetes de EF son la solución nativa de EF, pero FluentMigrator y DbUp diferencia clave: son herramientas específicas para la migración, mientras que los paquetes de EF provienen de su modelo de EF existente.
Uso --idempotent scripts:
dotnet ef migrations script --idempotent --output migrations.sql
El DBA revisa y aprueba.
Esa es una pregunta de estrategia de despliegue, no una pregunta de migraciones. Generalmente:
Los paquetes no resuelven esto, solo hacen el paso 3 más predecible.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.