Migrations

Generazione e aggiornamento dello schema di un database - Migrations

Dagli esempi mostrati precedentemente si è visto come sia possibile generare un database a partire dal modello dei dati. Ad esempio, nel caso dell’esempio GestioneFattureClienti trattato in precedenza, il mapping delle classi (Entity) sulle corrispondenti tabelle del database relazionale (Relational DBMS) viene eseguito per mezzo dell’operazione di migration:

1 dotnet ef migrations add InitialCreate

Una volta fatta la migration per creare il database fisico si può impartire il comando:

1 dotnet ef database update

Oppure, da codice (ad esempio nel Main), scrivendo una sequenza di istruzioni come la seguente (vedremo in seguito alcuni esempi…):

1 using (var db = new BloggingContext())
2          {
3              db.Database.Migrate();
4          }

In generale il meccanismo di migration è più sofisticato di quello che può apparire dagli esempi riportati precedentemente. Infatti, leggendo la documentazione Microsoft si evince che il meccanismo delle migrations è stato concepito per poter seguire l’evoluzione dello schema (ossia il modello) dei dati:

In real world projects, data models change as features get implemented: new entities or properties are added and removed, and database schemas need to be changed accordingly to be kept in sync with the application. The migrations feature in EF Core provides a way to incrementally update the database schema to keep it in sync with the application’s data model while preserving existing data in the database.

At a high level, migrations function in the following way:

  • When a data model change is introduced, the developer uses EF Core tools to add a corresponding migration describing the updates necessary to keep the database schema in sync. EF Core compares the current model against a snapshot of the old model to determine the differences, and generates migration source files; the files can be tracked in your project’s source control like any other source file.
  • Once a new migration has been generated, it can be applied to a database in various ways. EF Core records all applied migrations in a special history table, allowing it to know which migrations have been applied and which haven’t.

Di seguito si riporta un esempio di generazione di database con più migrations:
Let’s assume you’ve just completed your first EF Core application, which contains the following simple model:

  Ottieni il codice

 1//File Blog.cs
 2namespace MigrationsTest.Model;
 3public class Blog
 4{
 5    public int Id { get; set; }
 6    public string? Name { get; set; }
 7}
 8
 9//File BloggingContext.cs
10using Microsoft.EntityFrameworkCore;
11using MigrationsTest.Model;
12
13namespace MigrationsTest.Data;
14public class BloggingContext : DbContext
15{
16    public DbSet<Blog> Blogs { get; set; } = null!;
17    public string DbPath { get; }
18    public BloggingContext()
19    {
20        //https://www.hanselman.com/blog/how-do-i-find-which-directory-my-net-core-console-application-was-started-in-or-is-running-from
21        var folder = AppContext.BaseDirectory;
22        //La BaseDirectory restituisce la cartella dove si trova l'assembly (.dll e .exe del programma compilato)
23        //il database, per comodità, è inserito nella cartella di progetto, dove si trova anche il file Program.cs 
24        var path = Path.Combine(folder, "../../../blogs.db");
25        DbPath = path;
26    }
27    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
28    {
29        optionsBuilder.UseSqlite($"Data Source = {DbPath}");
30    }
31}

Si aggiungano i pacchetti per EF Core con SQLite:

1dotnet add package Microsoft.EntityFrameworkCore.Design
2dotnet add package Microsoft.EntityFrameworkCore.Sqlite

Si proceda alla creazione della prima migration:

1dotnet ef migrations add InitialCreate

Si potrà notare che nel progetto è stata creata la cartella Migrations con all’interno il codice per la creazione del database. La creazione del database fisico avviene con l’istruzione:

1dotnet ef database update

That’s all there is to it - your application is ready to run on your new database, and you didn’t need to write a single line of SQL. Note that this way of applying migrations is ideal for local development, but is less suitable for production environments - see the Applying Migrations page for more info.

Supponiamo ora di dover modificare il modello dei dati…

A few days have passed, and you’re asked to add a creation timestamp to your blogs. You’ve done the necessary changes to your application, and your model now looks like this:

1namespace MigrationsTest.Model;
2public class Blog
3{
4    public int Id { get; set; }
5    public string? Name { get; set; }
6    public DateTime CreatedTimestamp { get; set; }//👈 new property added
7}

Your model and your production database are now out of sync - we must add a new column to your database schema. Let’s create a new migration for this:

1dotnet ef migrations add AddBlogCreatedTimestamp

Note that we give migrations a descriptive name, to make it easier to understand the project history later. Since this isn’t the project’s first migration, EF Core now compares your updated model against a snapshot of the old model, before the column was added; the model snapshot is one of the files generated by EF Core when you add a migration, and is checked into source control. Based on that comparison, EF Core detects that a column has been added, and adds the appropriate migration. You can now apply your migration as before:

1dotnet ef database update