Applicazione di migrazioni

Dopo aver aggiunto le migrazioni, è necessario distribuirla e applicarla ai database. Esistono diverse strategie per eseguire questa operazione, con alcune più appropriate per gli ambienti di produzione e altre per il ciclo di vita di sviluppo.

Note

Indipendentemente dalla strategia di distribuzione, controllare sempre le migrazioni generate e testarle prima di applicarle a un database di produzione. Una migrazione può eliminare una colonna quando la finalità era rinominarla o potrebbe non riuscire per vari motivi quando viene applicata a un database.

Scegliere una strategia di distribuzione

Per la distribuzione automatizzata, usare un bundle di migrazione. Un bundle è un artefatto di distribuzione che può essere generato in CI ed eseguito in un secondo momento senza l'SDK di .NET, gli strumenti di EF Core o il codice sorgente dell'applicazione. Usare invece uno script SQL quando è necessario esaminare, modificare, archiviare o passare a un amministratore di database prima di applicarlo.

Per lo sviluppo locale o dotnet ef database updateUpdate-Database è in genere l'opzione più semplice. I progetti aspirano devono usare l'integrazione delle migrazioni di EF Core aspirare per coordinare l'esecuzione della migrazione locale e per pubblicare bundle o script.

Strategy Uso consigliato Esaminare SQL prima dell'esecuzione Richiede l'SDK e l'origine durante l'esecuzione Usa il blocco della migrazione di Entity Framework Esegue delegati di seeding di Entity Framework
Script SQL Distribuzione controllata o controllata dall'amministratore del database Yes No No No
Bundle di migrazione Distribuzione automatica No No Yes Yes
Strumenti da riga di comando di Entity Framework Sviluppo e test in locale No Yes Yes Yes
Migrazione in fase di esecuzione Applicazioni che accettano compromessi per la migrazione di avvio No No Yes Yes

EF Core 9 e versioni successive usano il blocco della migrazione. Le operazioni sincrone e gli strumenti richiamano UseSeeding; le operazioni asincrone richiamano UseAsyncSeeding.

Usare un'identità separata per la distribuzione con l'autorizzazione per modificare lo schema. L'identità usata dall'applicazione in fase di esecuzione deve normalmente avere solo le autorizzazioni necessarie all'applicazione per leggere e scrivere dati.

Script SQL

Gli script SQL sono consigliati quando il processo di distribuzione richiede l'ispezione o la modifica di SQL generato prima dell'esecuzione. I vantaggi di questa strategia includono quanto segue:

  • Gli script SQL possono essere esaminati per verificare l'accuratezza; questo aspetto è importante perché l'applicazione delle modifiche dello schema ai database di produzione è un'operazione potenzialmente pericolosa che potrebbe comportare la perdita di dati.
  • In alcuni casi, gli script possono essere ottimizzati per soddisfare le esigenze specifiche di un database di produzione.
  • Gli script SQL possono essere usati insieme a una tecnologia di distribuzione e possono anche essere generati come parte del processo di integrazione continua.
  • Gli script SQL possono essere forniti a un amministratore di database e possono essere gestiti e archiviati separatamente.

Utilizzo di base

Di seguito viene generato uno script SQL da un database vuoto alla migrazione più recente:

dotnet ef migrations script

Per impostazione predefinita, il comando scrive lo script nell'output standard. Usare --output (o -o) per creare un artefatto di distribuzione con un nome stimabile:

dotnet ef migrations script --idempotent --output artifacts/migrations.sql

Con from (destinazione implicita)

Di seguito viene generato uno script SQL dalla migrazione specificata alla migrazione più recente.

dotnet ef migrations script AddNewTables

Con Da e A

Di seguito viene generato uno script SQL dalla migrazione specificata from alla migrazione specificata to .

dotnet ef migrations script AddNewTables AddAuditTable

È possibile usare un from più recente rispetto a to per generare uno script di rollback.

Warning

Tenere conto degli scenari con potenziale perdita di dati.

La generazione di script accetta i due argomenti seguenti per indicare quale intervallo di migrazioni deve essere generato:

  • La migrazione di origine deve essere l'ultima migrazione applicata al database prima dell'esecuzione dello script. Se non è stata applicata alcuna migrazione, specificare 0 (valore predefinito).
  • La migrazione di destinazione è l'ultima migrazione applicata al database dopo l'esecuzione dello script. L'impostazione predefinita corrisponde all'ultima migrazione nel progetto.

Gli script di migrazione aggiornano un database esistente. Effettuare il provisioning del database stesso tramite il processo di distribuzione dell'infrastruttura o di amministrazione del database prima di applicare lo script. La creazione del database richiede in genere una connessione diversa, autorizzazioni elevate e una configurazione specifica del provider.

Script SQL Idempotenti

Gli script SQL generati in precedenza possono essere applicati solo per modificare lo schema da una migrazione a un'altra; è responsabilità dell'utente applicare lo script in modo appropriato e solo ai database nello stato di migrazione corretto. EF Core supporta anche la generazione di script idempotenti , che controllano internamente quali migrazioni sono già state applicate (tramite la tabella della cronologia delle migrazioni) e applicano solo quelle mancanti. Ciò è utile se non si conosce esattamente l'ultima migrazione applicata al database o se si esegue la distribuzione in più database che possono trovarsi in una migrazione diversa.

Il supporto dello script Idempotente dipende dal provider di database. Ad esempio, SQLite non supporta attualmente la generazione di script di migrazione idempotenti.

Di seguito vengono generate migrazioni idempotenti:

dotnet ef migrations script --idempotent

Strumenti da riga di comando

Gli strumenti da riga di comando di Entity Framework possono essere usati per applicare le migrazioni a un database. Sebbene sia produttiva per lo sviluppo locale e il test delle migrazioni, questo approccio non è ideale per la gestione dei database di produzione:

  • I comandi SQL vengono applicati direttamente dallo strumento, senza consentire allo sviluppatore di esaminarli o modificarli. Questo può essere pericoloso in un ambiente di produzione.
  • L'SDK .NET e lo strumento EF devono essere installati nei server di produzione e richiedono il codice sorgente del progetto.

Di seguito viene aggiornato il database alla migrazione più recente:

dotnet ef database update

Di seguito viene aggiornato il database a una determinata migrazione:

dotnet ef database update AddNewTables

Si noti che questa operazione può essere usata anche per eseguire il rollback a una migrazione precedente.

Warning

Tenere conto degli scenari con potenziale perdita di dati.

Per altre informazioni sull'applicazione delle migrazioni tramite gli strumenti da riga di comando, vedere le informazioni di riferimento sugli strumenti di EF Core.

Ambiente e configurazione

Gli strumenti eseguono il codice dell'applicazione per costruire .DbContext La selezione del provider, le stringhe di connessione e la configurazione del modello possono quindi dipendere dall'ambiente dell'applicazione. Gli strumenti in fase di progettazione di EF Core usano l'ambiente Development quando non è impostato né ASPNETCORE_ENVIRONMENTDOTNET_ENVIRONMENT .

Impostare l'ambiente in modo esplicito durante la generazione di un artefatto di distribuzione e quando si esegue un bundle. Ad esempio, in PowerShell:

$env:ASPNETCORE_ENVIRONMENT = 'Production'
dotnet ef migrations bundle --output artifacts\efbundle.exe
$env:ASPNETCORE_ENVIRONMENT = 'Production'
.\efbundle.exe --connection $env:DEPLOYMENT_CONNECTION_STRING

Oppure in una shell compatibile con POSIX:

ASPNETCORE_ENVIRONMENT=Production \
    dotnet ef migrations bundle --output artifacts/efbundle

ASPNETCORE_ENVIRONMENT=Production \
    ./efbundle --connection "$DEPLOYMENT_CONNECTION_STRING"

Ciò impedisce anche a un bundle di caricare i segreti utente di sviluppo in modo imprevisto. Un ambiente predefinito più sicuro per i bundle viene rilevato da dotnet/efcore#36188. La selezione dell'ambiente nell'esperienza di pubblicazione Visual Studio viene rilevata da dotnet/efcore#11950.

Non archiviare le stringhe di connessione di produzione nel controllo del codice sorgente o incorporarle nel bundle. Fornire la connessione di distribuzione dall'archivio segreto del sistema di distribuzione. L'identità di distribuzione deve disporre delle autorizzazioni dello schema; l'identità normale dell'applicazione in genere non dovrebbe.

Bundles

I bundle di migrazione sono eseguibili a file unico che possono essere utilizzati per applicare le migrazioni a un database. Affrontano alcune delle carenze dello script SQL e degli strumenti da riga di comando:

  • L'esecuzione di script SQL richiede strumenti aggiuntivi.
  • La gestione delle transazioni e il comportamento di errore continuo di questi strumenti sono incoerenti e talvolta imprevisti. Questo può lasciare il database in uno stato non definito se si verifica un errore durante l'applicazione delle migrazioni.
  • I bundle possono essere generati come parte del processo di integrazione continua e facilmente eseguiti in un secondo momento come parte del processo di distribuzione.
  • I bundle possono essere eseguiti senza installare .NET SDK o ef Tool (o anche il runtime .NET, se autonomo) e non richiedono il codice sorgente del progetto.
  • I bundle usano il blocco della migrazione di EF Core ed eseguono la logica configurata UseSeeding .

A differenza di uno script SQL, un bundle attualmente non fornisce un modo per esaminare il codice SQL che verrà eseguito o elencare le migrazioni in esso contenute. Se la distribuzione richiede la revisione di SQL, generare invece uno script. I miglioramenti di ispezione dei bundle vengono rilevati da dotnet/efcore#25872.

Di seguito viene generato un bundle:

dotnet ef migrations bundle --output artifacts/efbundle

Di seguito viene generato un bundle autonomo per Linux:

dotnet ef migrations bundle --self-contained --target-runtime linux-x64 --output artifacts/efbundle

Per ulteriori informazioni sulla creazione di bundle, vedere il riferimento sugli strumenti di EF Core.

efbundle

Il file eseguibile risultante è denominato efbundle per impostazione predefinita. Può essere usato per aggiornare il database alla migrazione più recente. Equivale all'esecuzione di dotnet ef database update o Update-Database.

Arguments:

Argument Description
<MIGRATION> Migrazione di destinazione. Se '0', tutte le migrazioni verranno ripristinate. Il valore predefinito è l'ultima migrazione.

Options:

Option Short Description
--connection <CONNECTION> La stringa di connessione al database. Il valore predefinito è quello specificato in AddDbContext o OnConfiguring.
--verbose -v Visualizzare l'output dettagliato.
--no-color Non colorare l'output.
--prefix-output Anteponi il livello all'output.

L'esempio seguente applica le migrazioni a un'istanza di SQL Server locale usando il nome utente e le credenziali specificati:

.\efbundle.exe --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;User ID=myUsername;Password={;'$Credential;'here'}'

Per eseguire il rollback del database, passare la migrazione che deve rimanere applicata. Il passaggio 0 ripristina tutte le migrazioni:

.\efbundle.exe PreviousMigration --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;Integrated Security=True'
.\efbundle.exe 0 --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;Integrated Security=True'

Warning

Un rollback esegue le Down operazioni di ogni migrazione più recente della destinazione e può comportare la perdita di dati. Esaminare e testare il comportamento di rollback prima di usarlo nei dati di produzione.

Il codice di seeding configurato viene eseguito dopo un downgrade. Deve tollerare lo schema della migrazione di destinazione, incluso uno schema dell'applicazione mancante quando la destinazione è 0.

Warning

Se la configurazione del contesto legge , copiare appsettings.jsoni file di impostazioni necessari insieme al bundle. I file di configurazione vengono risolti dalla directory di esecuzione del bundle. Non inserire segreti di produzione in questi file; fornire loro tramite un'origine di configurazione sicura o l'opzione --connection .

Contenitori e processi di distribuzione

Generare il bundle durante la compilazione ed eseguirlo come processo di distribuzione monotono dopo che il database è integro. Non installare l'SDK o eseguirlo dotnet ef nell'immagine dell'applicazione e non eseguire migrazioni da un punto di ingresso per ogni replica dell'applicazione. Configurare la piattaforma di distribuzione per non riavviare il contenitore di migrazione al termine dell'operazione.

Per le applicazioni Aspirabili, AddEFMigrations può coordinare le migrazioni durante lo sviluppo locale. Durante la pubblicazione, PublishAsMigrationBundle può generare un bundle o un'immagine del contenitore e PublishAsMigrationScript generare uno script SQL. Vedere Applicare le migrazioni di EF Core in Aspira per la configurazione di un processo one-shot per App contenitore di Azure, Docker Compose e Kubernetes.

L'esempio di bundle di migrazione illustra due migrazioni SQLite, il seeding idempotente, l'applicazione forward e il seeding rollback-safe.

Esempio di pacchetto di migrazione

Un bundle richiede di includere migrazioni. Questi vengono creati usando dotnet ef migrations add come descritto in Creare la prima migrazione. Dopo aver preparato le migrazioni per la distribuzione, crea un bundle utilizzando il dotnet ef migrations bundle. Per esempio:

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>

L'output è un eseguibile adatto per il sistema operativo di destinazione. Nel mio caso, si tratta di Windows x64, quindi mi viene inserito un efbundle.exe nella cartella locale. L'esecuzione di questo eseguibile applica le migrazioni contenute al suo interno:

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

Come con dotnet ef database update o Update-Database, le migrazioni vengono applicate al database solo se non sono già state applicate. Ad esempio, l'esecuzione dello stesso bundle non esegue di nuovo alcuna operazione, poiché non sono disponibili nuove migrazioni da applicare:

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

Tuttavia, se vengono apportate modifiche al modello e vengono generate più migrazioni con dotnet ef migrations add, questi possono essere raggruppati in un nuovo eseguibile pronto per l'applicazione. Per esempio:

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>

Tip

L'opzione --force può essere usata per sovrascrivere il bundle esistente con uno nuovo.

L'esecuzione di questo nuovo bundle applica queste due nuove migrazioni al database:

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

Per impostazione predefinita, il bundle usa il database stringa di connessione dalla configurazione dell'applicazione. Tuttavia, è possibile eseguire la migrazione di un database diverso passando il stringa di connessione nella riga di comando. Per esempio:

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>

Note

Questa volta sono state applicate tutte e tre le migrazioni, poiché nessuna di esse era ancora stata applicata al database di produzione.


Applicare migrazioni al runtime

È possibile che l'applicazione stessa applichi le migrazioni a livello di codice, in genere durante l'avvio. EF Core 9 e versioni successive proteggono l'esecuzione della migrazione con un blocco a livello di database, pertanto questo può essere accettabile per le applicazioni che preferiscono una distribuzione semplice e possono tollerare il comportamento di migrazione all'avvio. Un passaggio di distribuzione della migrazione separato è comunque preferibile quando è importante esaminare le credenziali con privilegi minimi, l'implementazione coordinata o la disponibilità elevata.

Considerare i compromessi seguenti:

  • Per le versioni di Entity Framework precedenti a 9, se più istanze dell'applicazione sono in esecuzione, entrambe le applicazioni potrebbero tentare di applicare la migrazione contemporaneamente e non riuscire (o peggio, causare il danneggiamento dei dati).
  • Analogamente, se un'applicazione accede al database mentre un'altra applicazione ne esegue la migrazione, questo può causare problemi gravi.
  • L'applicazione deve avere accesso con privilegi elevati per modificare lo schema del database. In genere è consigliabile limitare le autorizzazioni del database dell'applicazione nell'ambiente di produzione.
  • È importante poter eseguire il rollback di una migrazione applicata in caso di problema. Le altre strategie lo forniscono facilmente e pronto all'uso.
  • I comandi SQL vengono applicati direttamente dal programma, senza consentire allo sviluppatore di esaminarli o modificarli. Questo può essere pericoloso in un ambiente di produzione.

Per applicare migrazioni a livello di codice, chiamare context.Database.MigrateAsync(). Ad esempio, un'applicazione ASP.NET tipica può eseguire le operazioni seguenti:

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

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

    host.Run();
}

Si noti che MigrateAsync() si basa sul IMigrator servizio, che può essere usato per scenari più avanzati. Usare myDbContext.GetInfrastructure().GetService<IMigrator>() per accedervi.

Warning

  • Considerare attentamente prima di usare questo approccio nell'ambiente di produzione. Preferisce un bundle di migrazione per l'automazione o uno script SQL quando sono necessarie verifiche e approvazione.
  • Non chiamare EnsureCreatedAsync() prima di MigrateAsync(). EnsureCreatedAsync() ignora Migrations per creare lo schema, causando il fallimento di MigrateAsync().

Blocco della migrazione

A partire da EF Core 9, MigrateAsync e Migrate acquisiscono automaticamente un blocco a livello di database prima di applicare eventuali migrazioni. In questo modo si protegge dal danneggiamento del database che può derivare da più istanze dell'applicazione che eseguono migrazioni contemporaneamente, uno scenario comune quando si applicano migrazioni in fase di esecuzione. Il blocco viene mantenuto per la durata dell'esecuzione della migrazione, incluso qualsiasi codice di seeding e viene rilasciato automaticamente al termine dell'operazione.

Il blocco della migrazione si applica quando le migrazioni vengono applicate usando uno dei metodi seguenti:

Gli script SQL non sono interessati dal blocco della migrazione, perché vengono applicati all'esterno di EF Core.

Note

A partire da EF Core 9, la chiamata Migrate() o MigrateAsync() genererà un'eccezione quando il modello presenta modifiche in sospeso rispetto all'ultima migrazione (ID RelationalEventId.PendingModelChangesWarningevento di avviso). Per rilevare questa condizione prima della distribuzione, usare il dotnet ef migrations has-pending-model-changes comando nella pipeline CI/CD. L'avviso può essere eliminato tramite ConfigureWarnings (ignorando RelationalEventId.PendingModelChangesWarning) se necessario, ma questo non è in genere consigliato negli scenari di produzione. Per altre informazioni, vedere la nota di modifica che causa un'interruzione .

Warning

Il meccanismo di blocco varia in modo significativo tra i provider di database e può comportare problemi specifici del provider. Ad esempio, il provider SQLite usa una tabella di blocco che può essere abbandonata se il processo termina in modo imprevisto. Per informazioni dettagliate, consultare sempre la documentazione del provider.

Limitazioni