In esecuzione MigrateAsync() All'avvio? Stai dando i diritti del proprietario del database delle app e sperando che nulla vada storto. C'è un modo migliore - I pacchetti di migrazione EF ti permettono di eseguire le migrazioni come un passo CI controllato, mantenendo la tua app di produzione sicura. Ma ecco la cosa: a volte il modo "sbagliato" è in realtà bene. Esploriamo quando usare ogni approccio.
Documenti ufficiali: Panoramica delle migrazioni | Applicazione delle migrazioni | BundlesCity name (optional, probably does not need a translation)
Questo blog utilizza MigrateAsync() all'avvio - l'approccio che sto per dirti di non usare. Ecco perché va bene per me, e perché probabilmente non è per te.
Nella mia Program.cs file Ho il seguente:
using (var scope = app.Services.CreateScope())
{
var blogContext = scope.ServiceProvider.GetRequiredService<IMostlylucidDBContext>();
await blogContext.Database.MigrateAsync();
}
MigrateAsync() applica le migrazioni pendenti e crea la banca dati se necessario. Semplice - ma problematico:
db_owner Hai appena dato alla tua app runtime le chiavi per lasciare i tavoli.Perché la faccio franca: dati pubblici, rete Docker singola, progetto personale. Probabilmente non puoi.
Un pacchetto EF è un eseguibile autonomo contenente le migrazioni compilate. dotnet ef database update imballato in un standalone .exe.
Perché i pacchetti vincono:
db_owner; solo CI runner lo fa, solo durante l'implementazioneNota: Per la sicurezza di produzione, uso Identità gestita Invece delle stringhe di connessione. Ma i pacchetti sono ancora un passo avanti rispetto alle migrazioni runtime.
- 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 }}
Il bundle legge le stringhe di connessione dalle variabili di ambiente e applica le migrazioni pendenti. Già applicato? Esce con successo.
Vuoi testare prima di spingere? Costruisci bundle localmente.
Casi di utilizzo: Prova prima di CI, DBA handoff (exe autonomo, nessun SDK necessario), schieramento, debug 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
Errori di sintassi delle catture, violazioni dei vincoli, problemi FK - tutti prima CI o produzione.
La generazione del bundle è lenta - 30+ secondi su grandi progetti. Non generare su ogni build.
Se si desidera davvero auto-generazione, aggiungere un obiettivo MSBuild:
<Target Name="BuildMigrationBundle">
<Exec Command="dotnet ef migrations bundle --output $(OutputPath)efbundle.exe --force" />
</Target>
Poi: dotnet build -t:BuildMigrationBundle
Il meglio di entrambi i mondi: convenienza a livello locale, sicurezza nella produzione.
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
Generare SQL semplice invece di un eseguibile. Ottimo per la revisione DBA e i processi di gestione dei cambiamenti esistenti.
# 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
Pro: Visibilità completa, qualsiasi client SQL può eseguirlo, controllo di versione amichevole, flussi di lavoro di approvazione DBA.
Punti negativi: Nessuna tracciatura automatica (uso --idempotent), esecuzione manuale, potenziale deriva se gli script vengono modificati.
Vedi documenti ufficiali sugli script 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
DACPACsCity name (optional, probably does not need a translation) sono stato-based non basato sulla migrazione. Definisci lo schema desiderato, e SqlPackage lo differenzia dal database di destinazione.
SqlPackage.exe /Action:Publish /SourceFile:MyDatabase.dacpac /TargetConnectionString:"..."
Pro: Schema come codice, generazione auto-diff, gestisce tutto (tavoli, viste, SP, indici), strumenti aziendali.
Punti negativi: Solo SQL Server, schema in due posizioni (modello EF + progetto SQL), il motore diff fa scelte discutibili, le rinominazioni delle colonne sembrano drop+add.
Vedi Documenti SqlPackage.
| Avvicinamento | Migliore per | Richiede .NET | Auto-tracks Applied | DBA Friendly | Cross-platform DB |
|---|---|---|---|---|---|
MigrateAsync() |
Dev/small projects | Yes (runtime) | Yes | No | Yes |
| Bundles EF | Ci/CD pipelines | No (self-contained) | Sì | Somewhat | Sì |
| Script SQL | Ambienti controllati DBA | No | Con --idempotent |
Sì | Sì |
| DACPAC | SQL Server enterprise | No | Yes (state-based) | Yes | No |
Le migrazioni funzionano localmente ma non in CI? Controllare di aver commesso entrambi i file:
20231115_AddUserTable.cs - Il codice di migrazione20231115_AddUserTable.Designer.cs - L'istantanea del modelloManca il file Designer = guasto silenzioso.
dotnet ef migrations bundle --context BlogDbContext --output blog-migrations.exe
dotnet ef migrations bundle --context IdentityDbContext --output identity-migrations.exe
--connection argomentoappsettings.jsonUsare variabili d'ambiente in CI.
Gli strumenti EF devono istantiare il DbContext. Se il DbContext è in un progetto separato o ha un avvio complesso, implementare 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);
}
}
Utilizzare quando: DbContext in un progetto separato, startup complessa, bisogno di segreti utente per il design-time.
Domande comuni e risposte che ho ricevuto.
dotnet ef database update in CI?"Coperto sopra, ma la versione breve: bundle sono artefatti portatili. Il vostro passaggio di distribuzione non ha bisogno di EF CLI, codice sorgente, o la risoluzione del tempo di progettazione. Stesso bundle funziona in test, staging, e prod - zero deriva.
Se sei da solo, i dati sono pubblici, e il raggio d'esplosione e' basso... MigrateAsync() Ma nel momento in cui si aggiunge un secondo sviluppatore, dati sensibili, o più ambienti, pacchetti pagare per se stessi.
EF non fa rollback automatici. Opzioni:
Down() migrazione ed eseguirlo (ma devi averlo scritto)Per i sistemi critici: testare le migrazioni contro un clone di database prima.
Sì. Bundle + contenitore init è un modello solido:
initContainers:
- name: migrate
image: myapp:latest
command: ["./efbundle.exe"]
env:
- name: ConnectionStrings__Default
valueFrom:
secretKeyRef:
name: db-secrets
key: connection-string
Il contenitore dell'app attende che l'init completi.
Funzionano alla grande. I fasci EF sono la soluzione EF-native, ma FluentMigrator e DbUp hanno i loro fan. La differenza chiave: quelli sono strumenti specifici per la migrazione, mentre i pacchetti EF provengono dal modello EF esistente.
Uso --idempotent script:
dotnet ef migrations script --idempotent --output migrations.sql
DBA recensioni e approva. Quindi:
E' una questione di strategia di spiegamento, non una questione di migrazioni.
Bundles non risolvere questo - fanno solo il passo 3 più prevedibile.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.