Entity Framework - Migrazione Code First

Entity Framework 4.3 include una nuova funzionalità Code First Migrations che consente di far evolvere in modo incrementale lo schema del database man mano che il modello cambia nel tempo. Per la maggior parte degli sviluppatori, questo è un grande miglioramento rispetto alle opzioni di inizializzazione del database delle versioni 4.1 e 4.2 che richiedevano di aggiornare manualmente il database o rilasciarlo e ricrearlo quando il modello è cambiato.

  • Prima di Entity Framework 4.3, se si dispone già di dati (diversi dai dati seed) o di stored procedure esistenti, trigger, ecc. Nel database, queste strategie utilizzavano per eliminare l'intero database e ricrearlo, in modo da perdere i dati e altro DB oggetti.

  • Con la migrazione, aggiornerà automaticamente lo schema del database, quando il modello cambia senza perdere dati esistenti o altri oggetti del database.

  • Utilizza un nuovo inizializzatore di database chiamato MigrateDatabaseToLatestVersion.

Esistono due tipi di migrazione:

  • Migrazione automatizzata
  • Migrazione basata sul codice

Migrazione automatizzata

La migrazione automatizzata è stata introdotta per la prima volta in Entity Framework 4.3. Nella migrazione automatica non è necessario elaborare manualmente la migrazione del database nel file di codice. Ad esempio, per ogni modifica dovrai anche modificare le classi del tuo dominio. Ma con la migrazione automatizzata devi solo eseguire un comando nella console di Gestione pacchetti per farlo.

Diamo uno sguardo al seguente processo passo passo di migrazione automatizzata.

Quando utilizzi l'approccio Code First, non hai un database per la tua applicazione.

In questo esempio inizieremo con le nostre 3 classi di base come Studente, Corso e Iscrizione come mostrato nel codice seguente.

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }

}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

Di seguito è riportata la classe di contesto.

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Prima di eseguire l'applicazione, è necessario abilitare la migrazione automatica.

Step 1 - Aprire la console di Package Manager da Strumenti → NuGet Package Manager → Console di Package Manager.

Step 2 - Per abilitare la migrazione automatica, eseguire il seguente comando nella console di Gestione pacchetti.

PM> enable-migrations -EnableAutomaticMigrations:$true

Step 3 - Una volta eseguito correttamente il comando, crea una classe di configurazione sigillata interna nella cartella Migration del progetto, come mostrato nel codice seguente.

namespace EFCodeFirstDemo.Migrations {

   using System;
   using System.Data.Entity;
   using System.Data.Entity.Migrations;
   using System.Linq;
	
   internal sealed class Configuration : DbMigrationsConfiguration<EFCodeFirstDemo.MyContext> {

      public Configuration() {
         AutomaticMigrationsEnabled = true;
         ContextKey = "EFCodeFirstDemo.MyContext";
      }

      protected override void Seed(EFCodeFirstDemo.MyContext context) {

         //  This method will be called after migrating to the latest version.
         //  You can use the DbSet<T>.AddOrUpdate() helper extension method
         //  to avoid creating duplicate seed data. E.g.

         //  context.People.AddOrUpdate(
            //  p ⇒ p.FullName, 
            //  new Person { FullName = "Andrew Peters" }, 
            //  new Person { FullName = "Brice Lambson" }, 
            //  new Person { FullName = "Rowan Miller" }
         //  );
      }
   }
}

Step 4 - Impostare l'inizializzatore del database nella classe di contesto con la nuova strategia di inizializzazione del database MigrateDatabaseToLatestVersion.

public class MyContext : DbContext {

   public MyContext() : base("MyContextDB") {
      Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, 
         EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

}

Step 5- Hai impostato la migrazione automatica. Quando esegui la tua applicazione, si occuperà automaticamente della migrazione, quando modifichi il modello.

Step 6- Come puoi vedere, una tabella di sistema __MigrationHistory viene creata anche nel tuo database con altre tabelle. In __MigrationHistory, la migrazione automatizzata conserva la cronologia delle modifiche al database.

Step 7- Quando aggiungi un'altra classe di entità come classe di dominio ed esegui la tua applicazione, verrà creata la tabella nel tuo database. Aggiungiamo la seguente classe StudentLogIn.

public class StudentLogIn {
   [Key, ForeignKey("Student")]
   public int ID { get; set; }
   public string EmailID { get; set; }
   public string Password { get; set; }
	
   public virtual Student Student { get; set; }
}

Step 8 - Non dimenticare di aggiungere il DBSet per la classe sopra menzionata nella tua classe di contesto come mostrato nel codice seguente.

public virtual DbSet<StudentLogIn> StudentsLogIn { get; set; }

Step 9 - Esegui di nuovo l'applicazione e vedrai che la tabella StudentsLogIn viene aggiunta al tuo database.

I passaggi sopra menzionati per le migrazioni automatiche funzioneranno solo per la tua entità. Ad esempio, per aggiungere un'altra classe di entità o rimuovere la classe di entità esistente, la migrazione verrà eseguita correttamente. Ma se aggiungi o rimuovi qualsiasi proprietà alla tua classe di entità, verrà generata un'eccezione.

Step 10 - Per gestire la migrazione delle proprietà è necessario impostare AutomaticMigrationDataLossAllowed = true nel costruttore della classe di configurazione.

public Configuration() {
   AutomaticMigrationsEnabled = true;
   AutomaticMigrationDataLossAllowed = true;
   ContextKey = "EFCodeFirstDemo.MyContext";
}

Migrazione basata sul codice

Quando si sviluppa una nuova applicazione, il modello di dati cambia frequentemente e ogni volta che il modello cambia, non è più sincronizzato con il database. È stato configurato Entity Framework per eliminare e ricreare automaticamente il database ogni volta che si modifica il modello di dati. La migrazione basata sul codice è utile quando si desidera un maggiore controllo sulla migrazione.

  • Quando aggiungi, rimuovi o modifichi classi di entità o modifichi la classe DbContext, la prossima volta che esegui l'applicazione, il database esistente verrà eliminato automaticamente, ne crea uno nuovo che corrisponde al modello e lo semi con i dati di test.

  • La funzionalità Code First Migrations risolve questo problema consentendo a Code First di aggiornare lo schema del database invece di eliminare e ricreare il database. Per distribuire l'applicazione, dovrai abilitare le migrazioni.

Ecco la regola di base per migrare le modifiche nel database:

  • Abilita migrazioni
  • Aggiungi migrazione
  • Aggiornare il database

Diamo un'occhiata al seguente processo passo passo della migrazione della base di codice.

Quando si utilizza l'approccio code first, non si dispone di un database per la propria applicazione.

In questo esempio inizieremo di nuovo con le nostre 3 classi di base come Studente, Corso e Iscrizione come mostrato nel codice seguente.

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }

}

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   [Index]
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

Di seguito è riportata la classe di contesto.

public class MyContext : DbContext {

   public MyContext() : base("MyContextDB") {
      Database.SetInitializer(new MigrateDatabaseToLatestVersion<
         MyContext, EFCodeFirstDemo.Migrations.Configuration>("MyContextDB"));
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }

}

Step 1 - Prima di eseguire l'applicazione è necessario abilitare la migrazione.

Step 2 - Aprire la console di Gestione pacchetti da Strumenti → Gestione pacchetti NuGet → Console Gestione pacchetti.

Step 3 - La migrazione è già abilitata, ora aggiungi la migrazione nella tua applicazione eseguendo il seguente comando.

PM> add-migration "UniDB Schema"

Step 4 - Quando il comando viene eseguito con successo, vedrai che è stato creato un nuovo file nella cartella Migration con il nome del parametro passato al comando con un prefisso timestamp come mostrato nell'immagine seguente.

Step 5 - È possibile creare o aggiornare il database utilizzando il comando "update-database".

PM> Update-Database -Verbose

Il flag "-Verbose" specifica di mostrare le istruzioni SQL applicate al database di destinazione nella console.

Step 6 - Aggiungiamo un'altra proprietà "Età" nella classe dello studente e quindi eseguiamo l'istruzione di aggiornamento.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public int Age { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }

}

Quando esegui PM → Update-Database –Verbose, quando il comando è stato eseguito con successo vedrai che la nuova colonna Age è stata aggiunta nel tuo database.

Si consiglia di eseguire l'esempio precedente in modo graduale per una migliore comprensione.