Gestione delle migrazioni

Man mano che il modello cambia, le migrazioni vengono aggiunte e rimosse durante lo sviluppo normale e i file di migrazione vengono controllati nel controllo del codice sorgente del progetto. Per gestire le migrazioni, è prima necessario installare gli strumenti da riga di comando di EF Core.

Suggerimento

Se DbContext si trova in un assembly diverso da quello del progetto di avvio, è possibile specificare in modo esplicito i progetti di destinazione e di avvio negli strumenti della console di Gestione pacchetti o negli strumenti dell'interfaccia della riga di comando di .NET Core.

Aggiungere una migrazione

Dopo aver modificato il modello, è possibile aggiungere una migrazione per tale modifica:

dotnet ef migrations add AddBlogCreatedTimestamp

Il nome della migrazione può essere usato come messaggio di commit in un sistema di controllo della versione. Ad esempio, è possibile scegliere un nome come AddBlogCreatedTimestamp se la modifica è una nuova CreatedTimestamp proprietà nell'entità Blog .

Nella directory Migrations del progetto vengono aggiunti tre file:

  • XXXXXXXXXXXXXX_AddCreatedTimestamp.cs--File principale delle migrazioni. Contiene le operazioni necessarie per applicare la migrazione (in Up) e per ripristinarla (in Down).
  • XXXXXXXXXXXXXX_AddCreatedTimestamp.Designer.cs--File di metadati delle migrazioni. Contiene informazioni usate da Entity Framework.
  • MyContextModelSnapshot.cs: snapshot del modello corrente. Usato per determinare cosa è stato modificato durante l'aggiunta della migrazione successiva.

Il timestamp nel nome file consente di mantenerne l'ordine cronologico e di visualizzare quindi la successione delle modifiche.

Spazi dei nomi

È consentito spostare i file della cartella Migrations e modificarne lo spazio dei nomi manualmente. Le nuove migrazioni vengono create come migrazioni di pari livello rispetto all'ultima. In alternativa, è possibile specificare la directory in fase di generazione come indicato di seguito:

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

Nota

È anche possibile modificare lo spazio dei nomi indipendentemente dalla directory usando --namespace.

Personalizzare il codice di migrazione

Mentre EF Core crea in genere migrazioni accurate, è consigliabile esaminare sempre il codice e assicurarsi che corrisponda alla modifica desiderata; in alcuni casi, è anche necessario farlo.

Rinomina la colonna

Un esempio importante in cui è necessaria la personalizzazione delle migrazioni è la ridenominazione di una proprietà. Ad esempio, se si rinomina una proprietà da Name a FullName, EF Core genererà la migrazione seguente:

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

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

EF Core in genere non è in grado di sapere quando l'intenzione è eliminare una colonna e crearne una nuova (due modifiche separate) e quando una colonna deve essere rinominata. Se la migrazione precedente viene applicata così come è, tutti i nomi dei clienti andranno persi. Per rinominare una colonna, sostituire la migrazione generata in precedenza con quanto segue:

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

Suggerimento

Durante il processo di scaffolding della migrazione viene visualizzato un avviso se un'operazione può causare una perdita di dati, ad esempio nel caso della rimozione di una colonna. Se viene visualizzato questo avviso, assicurarsi in particolare di esaminare il codice delle migrazioni per verificarne l'accuratezza.

Aggiunta di SQL non elaborato

Anche se la ridenominazione di una colonna può essere ottenuta tramite un'API predefinita, in molti casi non è possibile. Ad esempio, è possibile sostituire le proprietà esistenti FirstName e LastName con una singola proprietà nuova FullName . La migrazione generata da EF Core sarà la seguente:

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

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

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

Come in precedenza, ciò causerebbe una perdita di dati indesiderata. Per trasferire i dati dalle colonne precedenti, vengono riorganizzare le migrazioni e introdurre un'operazione SQL non elaborata come indicato di seguito:

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

migrationBuilder.Sql(
@"
    UPDATE Customer
    SET FullName = FirstName + ' ' + LastName;
");

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

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

Modifiche arbitrarie tramite SQL non elaborato

SQL non elaborato può essere usato anche per gestire gli oggetti di database di cui EF Core non è a conoscenza. A tale scopo, aggiungere una migrazione senza apportare alcuna modifica al modello; verrà generata una migrazione vuota, che sarà quindi possibile popolare con operazioni SQL non elaborate.

Ad esempio, la migrazione seguente crea una stored procedure di SQL Server:

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

Suggerimento

EXEC viene usato quando un'istruzione deve essere la prima o una sola in un batch SQL. Può anche essere usato per risolvere gli errori del parser negli script di migrazione idempotenti che possono verificarsi quando le colonne a cui si fa riferimento non esistono attualmente in una tabella.

Può essere usato per gestire qualsiasi aspetto del database, tra cui:

  • Stored procedure
  • Ricerca full-text
  • Funzioni
  • Trigger
  • Visualizzazioni

Nella maggior parte dei casi, EF Core esegue automaticamente il wrapping di ogni migrazione nella propria transazione quando si applicano le migrazioni. Sfortunatamente, alcune operazioni di migrazione non possono essere eseguite all'interno di una transazione in alcuni database; per questi casi, è possibile rifiutare esplicitamente la transazione passando suppressTransaction: true a migrationBuilder.Sql.

Rimuovere una migrazione

Dopo l'aggiunta di una migrazione ci si rende talvolta conto che prima di applicarla sono necessarie altre modifiche al modello di Entity Framework Core. Per rimuovere l'ultima migrazione, usare questo comando.

dotnet ef migrations remove

Dopo la rimozione della migrazione, è possibile apportare le modifiche aggiuntive al modello. La migrazione può quindi essere aggiunta di nuovo.

Avviso

Evitare di rimuovere eventuali migrazioni già applicate ai database di produzione. In questo modo non sarà possibile ripristinare tali migrazioni dai database e potrebbe interrompere i presupposti effettuati dalle migrazioni successive.

Elenco delle migrazioni

È possibile elencare tutte le migrazioni esistenti come indicato di seguito:

dotnet ef migrations list

Controllo delle modifiche al modello in sospeso

Nota

Questa funzionalità è stata aggiunta in EF Core 8.0.

In alcuni casi potrebbe essere necessario verificare se sono state apportate modifiche al modello dall'ultima migrazione. Ciò può aiutare a sapere quando si è dimenticato di aggiungere una migrazione da parte di un collega o di un collega. Un modo per eseguire questa operazione consiste nell'usare questo comando.

dotnet ef migrations has-pending-model-changes

È anche possibile eseguire questo controllo a livello di codice usando context.Database.HasPendingModelChanges(). Può essere usato per scrivere uno unit test che non riesce quando si dimentica di aggiungere una migrazione.

Reimpostazione di tutte le migrazioni

In alcuni casi estremi potrebbe essere necessario rimuovere tutte le migrazioni e ricominciare. Questa operazione può essere eseguita facilmente eliminando la cartella Migrations e rilasciando il database. A questo punto è possibile creare una nuova migrazione iniziale, che conterrà l'intero schema corrente.

È anche possibile reimpostare tutte le migrazioni e crearne una singola senza perdere i dati. Questa operazione è talvolta denominata "squashing" e comporta alcune operazioni manuali:

  1. Eseguire il backup del database, nel caso in cui si sia verificato un errore.
  2. Nel database eliminare tutte le righe dalla tabella della cronologia delle migrazioni, ad esempio DELETE FROM [__EFMigrationsHistory] in SQL Server.
  3. Eliminare la cartella Migrations .
  4. Creare una nuova migrazione e generare uno script SQL (dotnet ef migrations script).
  5. Inserire una singola riga nella cronologia delle migrazioni per registrare che la prima migrazione è già stata applicata, poiché le tabelle sono già presenti. L'istruzione SQL di inserimento è l'ultima operazione nello script SQL generato in precedenza e è simile alla seguente (non dimenticare di aggiornare i valori):
INSERT INTO [__EFMigrationsHistory] ([MIGRATIONID], [PRODUCTVERSION])
VALUES (N'<full_migration_timestamp_and_name>', N'<EF_version>');

Avviso

Qualsiasi codice di migrazione personalizzato andrà perso quando la cartella Migrations viene eliminata. Per conservare le eventuali personalizzazioni, è necessario applicare manualmente la nuova migrazione iniziale.

Risorse aggiuntive