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:
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:
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
Note that this time, EF detects that the database already exists. In addition, when our first migration was applied above, this fact was recorded in a special migrations history table in your database; this allows EF to automatically apply only the new migration.
È anche possibile escludere dei tipi (Entity) da una migrazione.