Getting started

Entity Framework (EF) Core overview and first examples

Overview

Entity Framework (EF) Core is a lightweight, extensible, open source and cross-platform version of the popular Entity Framework data access technology1.

EF Core can serve as an object-relational mapper (O/RM), which:

  • Enables .NET developers to work with a database using .NET objects.
  • Eliminates the need for most of the data-access code that typically needs to be written.

EF Core supports many database engines, see Database Providers for details.

The model

With EF Core, data access is performed using a model. A model is made up of entity classes and a context object that represents a session with the database. The context object allows querying and saving data. For more information, see Creating a Model.

EF supports the following model development approaches:

  • Generate a model from an existing database.
  • Hand code a model to match the database.
  • Once a model is created, use EF Migrations to create a database from the model. Migrations allow evolving the database as the model changes.

A titolo illustrativo si riporta di seguito un esempio di uso del framework EF Core. Alcuni dettagli saranno chiariti in seguito.

 1using System.Collections.Generic;
 2using Microsoft.EntityFrameworkCore;
 3
 4namespace Intro;
 5//definizione del DbContext
 6public class BloggingContext : DbContext
 7{
 8    public DbSet<Blog> Blogs { get; set; }
 9    public DbSet<Post> Posts { get; set; }
10
11    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
12    {
13        optionsBuilder.UseSqlServer(
14          //questa è la stringa di connessione: varia in base al tipo di database utilizzato e alle configurazioni di accesso - maggiori dettagli in seguito...
15            @"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
16    }
17}
18//definizione del data model
19public class Blog
20{
21    public int BlogId { get; set; }
22    public string Url { get; set; }
23    public int Rating { get; set; }
24    public List<Post> Posts { get; set; }
25}
26
27public class Post
28{
29    public int PostId { get; set; }
30    public string Title { get; set; }
31    public string Content { get; set; }
32
33    public int BlogId { get; set; }
34    public Blog Blog { get; set; }
35}

Un esempio di come si usa EF Core

Querying

Instances of your entity classes are retrieved from the database using Language Integrated Query (LINQ). For more information, see Querying Data.

1using (var db = new BloggingContext())
2{
3    var blogs = db.Blogs
4        .Where(b => b.Rating > 3)
5        .OrderBy(b => b.Url)
6        .ToList();
7}

Saving data

Data is created, deleted, and modified in the database using instances of your entity classes. See Saving Data to learn more.

1using (var db = new BloggingContext())
2{
3    var blog = new Blog { Url = "http://sample.com" };
4    db.Blogs.Add(blog);
5    db.SaveChanges();
6}

EF Core Model Tutorial 1 - Console App

  Ottieni il codice

In this tutorial, you create a .NET Core console app that performs data access against a SQLite database using Entity Framework Core2.

Prerequisiti

Visual Studio Code con:

  • .NET SDK 8 o superiore
  • C# Dev Kit

Create a new solution with a project

1# creiamo una nuova soluzione
2dotnet new sln -o EFGetStarted
3# spostiamoci nella cartella della soluzione
4cd EFGetStarted
5# creiamo un nuovo progetto di tipo console all'interno della soluzione
6dotnet new console -o EFGetStarted
7# aggiungiamo il progetto alla soluzione
8dotnet sln add EFGetStarted\EFGetStarted.csproj

Install Entity Framework Core

To install EF Core, you install the package for the EF Core database provider(s) you want to target. This tutorial uses SQLite because it runs on all platforms that .NET supports. For a list of available providers, see Database Providers.

1dotnet add package Microsoft.EntityFrameworkCore.Sqlite

Configure the Proxy Server

1# per impostare il proxy a scuola
2$env:http_proxy="proxy.intranet:3128"
3$env:https_proxy="proxy.intranet:3128"
4# per verificare il valore delle variabili
5echo $env:http_proxy
6echo $env:https_proxy
7# le variabili impostate in questo modo sono attive per la sessione corrente
1# per impostare il proxy a scuola
2http_proxy="proxy.intranet:3128"
3https_proxy="proxy.intranet:3128"
4# per verificare il valore delle variabili
5echo $http_proxy
6echo $https_proxy
1:: per impostare il proxy a scuola
2set http_proxy=proxy.intranet:3128
3set https_proxy=proxy.intranet:3128
4:: per verificare il valore delle variabili
5echo %http_proxy%
6echo %https_proxy%

Create the model

Define a context class and entity classes that make up the model.

  • In the project directory, create Model.cs with the following code
 1//file: Model.cs
 2using Microsoft.EntityFrameworkCore;
 3
 4namespace EFGetStarted;
 5public class BloggingContext : DbContext
 6{
 7    public DbSet<Blog> Blogs { get; set; } = null!;
 8    public DbSet<Post> Posts { get; set; } = null!;
 9    public string DbPath { get; }
10    public BloggingContext()
11    {
12        var folder = Environment.SpecialFolder.LocalApplicationData;
13        var path = Environment.GetFolderPath(folder);
14        DbPath = System.IO.Path.Join(path, "blogging.db");
15    }
16    // The following configures EF to create a Sqlite database file in the
17    // special "local" folder for your platform.
18    protected override void OnConfiguring(DbContextOptionsBuilder options)
19        => options.UseSqlite($"Data Source={DbPath}");
20}
21
22public class Blog
23{
24    public int BlogId { get; set; }
25    public string? Url { get; set; }
26    public List<Post> Posts { get; } = new();
27}
28
29public class Post
30{
31    public int PostId { get; set; }
32    public string? Title { get; set; }
33    public string? Content { get; set; }
34
35    public int BlogId { get; set; }
36    public Blog Blog { get; set; } = null!;
37}

EF Core can also reverse engineer a model from an existing database.

Tip: This application intentionally keeps things simple for clarity. Connection strings should not be stored in the code for production applications. You may also want to split each C# class into its own file.

Create the database

1//File: Program.cs
2using EFGetStarted;
3
4using var db = new BloggingContext();

The following steps use migrations to create a database.

  • Run the following commands in .NET CLI

     1# installiamo il pacchetto dotnet-ef a livello globale
     2dotnet tool install --global dotnet-ef
     3
     4# nel caso il pacchetto dotnet-ef fosse già installato e si volesse effettuare un aggiornamento il comando è
     5dotnet tool update --global dotnet-ef
     6
     7# installiamo il pacchetto Design
     8dotnet add package Microsoft.EntityFrameworkCore.Design
     9
    10# effettuiamo la prima migrazione
    11dotnet ef migrations add InitialCreate
    12
    13# creiamo il database a partire dalla migrazione
    14dotnet ef database update
    

This installs dotnet ef and the design package which is required to run the command on a project. The migrations command scaffolds a migration to create the initial set of tables for the model. The database update command creates the database and applies the new migration to it.

Per aprire il file del database di SQLite, si utilizzi il programma DB Browser, cliccando sul pulsante Open Database

DB Browser Open Database

Si selezioni il file del database di SQLite e si osservi le tabelle create:

Created Tables in DB Browser

Cliccando sul pulsante Browse Data si vede che non ci sono ancora dati nel database.

  • Inserire dati nel database.
    Per inserire dati nel database si modifichi il file Program.cs con il seguente codice:

     1//File: Program.cs
     2using EFGetStarted;
     3
     4using var db = new BloggingContext();
     5// Note: This sample requires the database to be created before running.
     6Console.WriteLine($"Database path: {db.DbPath}.");
     7
     8// Create
     9Console.WriteLine("Inserting a new blog");
    10db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
    11db.SaveChanges();
    12
    13// Read
    14Console.WriteLine("Querying for a blog");
    15var blog = db.Blogs
    16    .OrderBy(b => b.BlogId)
    17    .First();
    18Console.WriteLine($"blog: {blog.Url}");
    

    Si mandi in esecuzione il progetto (da Visual Studio Code, oppure dalla command line mediante il comando dotnet run eseguito nella shell posizionata sulla cartella del progetto) e si osservi il contenuto della tabella Blogs dal programma DB Browser for SQLite:

    First value inserted in Blogs table

  • Modificare il contenuto del blog con BlogId=1 e aggiungere un post associato a questo blog.
    Per ottenere questo risultato si modifichi il codice del file Program.cs come segue:

     1using EFGetStarted;
     2
     3using var db = new BloggingContext();
     4// Note: This sample requires the database to be created before running.
     5Console.WriteLine($"Database path: {db.DbPath}.");
     6
     7// Create
     8//Console.WriteLine("Inserting a new blog");
     9//db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
    10//db.SaveChanges();
    11
    12//// Read
    13Console.WriteLine("Querying for a blog");
    14var blog = db.Blogs
    15    .OrderBy(b => b.BlogId)
    16    .First();
    17Console.WriteLine($"blog: {blog.Url}");
    18
    19// Update
    20Console.WriteLine("Updating the blog and adding a post");
    21blog.Url = "https://devblogs.microsoft.com/dotnet";
    22blog.Posts.Add(
    23    new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
    24db.SaveChanges();
    
  • Cancellare un blog e tutti i post ad esso associati.
    Per ottenere questo risultato si modifichi il codice del file Program.cs come segue:

     1using EFGetStarted;
     2
     3using var db = new BloggingContext();
     4// Note: This sample requires the database to be created before running.
     5Console.WriteLine($"Database path: {db.DbPath}.");
     6
     7// Create
     8//Console.WriteLine("Inserting a new blog");
     9//db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
    10//db.SaveChanges();
    11
    12//// Read
    13Console.WriteLine("Querying for a blog");
    14var blog = db.Blogs
    15    .OrderBy(b => b.BlogId)
    16    .First();
    17Console.WriteLine($"blog: {blog.Url}");
    18
    19// Update
    20//Console.WriteLine("Updating the blog and adding a post");
    21//blog.Url = "https://devblogs.microsoft.com/dotnet";
    22//blog.Posts.Add(
    23//    new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
    24//db.SaveChanges();
    25
    26// Delete
    27Console.WriteLine("Delete the blog");
    28db.Remove(blog);
    29db.SaveChanges();
    

    Si può osservare che il database è ritornato ad essere vuoto.

EF Core Model Tutorial 2 - Gestione Fatture e Clienti

  Ottieni il codice

Creazione progetto

Come nell’esempio precedente, creiamo un progetto Console .NET Core

 1# creiamo una nuova soluzione
 2dotnet new sln -o GestioneFattureClienti
 3# spostiamoci nella cartella della soluzione
 4cd GestioneFattureClienti
 5# creiamo un nuovo progetto di tipo console all'interno della soluzione
 6dotnet new console -o GestioneFattureClienti
 7# aggiungiamo il progetto alla soluzione
 8dotnet sln add GestioneFattureClienti\GestioneFattureClienti.csproj
 9# facciamo partire Visual Studio Code sulla soluzione
10code .

Installazione di Entity Framework Core

Dalla shell posizionata sulla cartella del progetto GestioneFattureClienti, installiamo i seguenti pacchetti nel progetto:

1# installiamo il pacchetto Design
2# https://www.nuget.org/packages/microsoft.entityframeworkcore.design/
3dotnet add package Microsoft.EntityFrameworkCore.Design
4# installiamo il pacchetto per Sqlite
5# https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite
6dotnet add package Microsoft.EntityFrameworkCore.Sqlite

Creazione del modello

  • In Visual Studio Code, tramite C# Dev Kit, creare la cartella Model e al suo interno aggiungere la classe Cliente nel file Cliente.cs con il seguente codice:

     1namespace GestioneFattureClienti.Model;
     2public class Cliente
     3{
     4    public int ClienteId { get; set; }
     5    public string RagioneSociale { get; set; } = null!;
     6    public string PartitaIVA { get; set; } = null!;
     7    public string? Citta { get; set; }
     8    public string? Via { get; set; }
     9    public string? Civico { get; set; }
    10    public string? CAP { get; set; }
    11    public List<Fattura> Fatture { get; } = [];
    12
    13    public override string ToString()
    14    {
    15        return $"{{{nameof(ClienteId)} = {ClienteId}, " +
    16            $"{nameof(RagioneSociale)} = {RagioneSociale}, " +
    17            $"{nameof(PartitaIVA)} = {PartitaIVA}, " +
    18            $"{nameof(Citta)} = {Citta}, " +
    19            $"{nameof(Via)} = {Via}, " +
    20            $"{nameof(Civico)} = {Civico}, " +
    21            $"{nameof(CAP)} = {CAP}}}";
    22    }
    23}

  • Aggiungere la classe Fattura nel file Fattura.cs all’interno della cartella Model, con dentro il seguente codice:

     1namespace GestioneFattureClienti.Model;
     2
     3public class Fattura
     4{
     5    public int FatturaId { get; set; }
     6    public DateTime Data { get; set; }
     7    public decimal Importo { get; set; }
     8    public int ClienteId { get; set; }
     9    public Cliente Cliente { get; set; } = null!;
    10
    11    public override string ToString()
    12    {
    13        return $"{{{nameof(FatturaId)} = {FatturaId}, " +
    14            $"{nameof(Data)} = {Data.ToShortDateString()}, " +
    15            $"{nameof(Importo)} = {Importo}, " +
    16            $"{nameof(ClienteId)} = {ClienteId}}}";
    17    }
    18}

  • In Visual Studio Code, tramite C# Dev Kit, creare la cartella Data e al suo interno aggiungere la classe FattureClientiContext nel file FattureClientiContext.cs con il codice:

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

A questo punto nel progetto dovrebbe essere presente una struttura di file e cartelle come quella riportata nella figura seguente:

Solution Explorer Image

  • Eseguire il seguente comando nella shell posizionata sulla cartella del progetto

    1# effettuiamo la prima migrazione
    2dotnet ef migrations add InitialCreate
    3
    4# creiamo il database a partire dalla migrazione
    5dotnet ef database update
    

Una volta eseguita la migration e aggiornato il database, dovrebbe essere presente un file di database di SQLite FattureClienti.db con le tabelle corrispondenti agli oggetti Fatture e Clienti (di tipo DBSet<Fattura> e DBSet<Cliente> rispettivamente). Dopo aver eseguito la migration e l’aggiornamento del database, la struttura dei file e delle cartelle dovrebbe essere come quella della figura seguente:

Solution Explorer Image complete

Interazione con la base di dati

  • Si scriva il codice nella classe Program per testare le funzionalità di EF Core con SQLite, come nell’esempio seguente:
      1using GestioneFattureClienti.Data;
      2using System.Text;
      3using GestioneFattureClienti.Model;
      4using Microsoft.EntityFrameworkCore.Storage;
      5using Microsoft.EntityFrameworkCore.Infrastructure;
      6
      7Console.OutputEncoding = Encoding.UTF8;
      8Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("it-IT");
      9
     10ModalitàOperativa modalitàOperativa = ModalitàOperativa.Nessuna;
     11//colore di default della console
     12Console.ForegroundColor = ConsoleColor.Cyan;
     13//gestione del menu
     14bool uscitaDalProgramma = false;
     15do
     16{
     17	//gestione della scelta 
     18	bool correctInput = false;
     19	do
     20	{
     21		Console.WriteLine("Inserire la modalità operativa [CreazioneDb, LetturaDb, ModificaFattura, CancellazioneFattura, CancellazioneDb, Nessuna]");
     22		correctInput = Enum.TryParse(Console.ReadLine(), true, out modalitàOperativa);
     23		if (correctInput)
     24		{
     25			switch (modalitàOperativa)
     26			{
     27				case ModalitàOperativa.CreazioneDb:
     28					CreazioneDb();
     29					break;
     30				case ModalitàOperativa.LetturaDb:
     31					LetturaDb();
     32					break;
     33				case ModalitàOperativa.ModificaFattura:
     34					ModificaFattura();
     35					break;
     36				case ModalitàOperativa.CancellazioneFattura:
     37					CancellazioneFattura();
     38					break;
     39				case ModalitàOperativa.CancellazioneDb:
     40					CancellazioneDb();
     41					break;
     42				default:
     43					WriteLineWithColor("Non è stata impostata nessuna modalità operativa", ConsoleColor.Yellow);
     44					break;
     45			}
     46		}
     47		if (!correctInput)
     48		{
     49			Console.Clear();
     50			WriteLineWithColor("Il valore inserito non corrisponde a nessuna opzione valida.\nI valori ammessi sono: [Creazione, Lettura, Modifica, Cancellazione, Nessuna]", ConsoleColor.Red);
     51		}
     52	} while (!correctInput);
     53	Console.WriteLine("Uscire dal programma?[Si, No]");
     54	uscitaDalProgramma = Console.ReadLine()?.StartsWith("si", StringComparison.CurrentCultureIgnoreCase) ?? false;
     55	Console.Clear();
     56} while (!uscitaDalProgramma);
     57
     58static void CreazioneDb()
     59{
     60	using FattureClientiContext db = new();
     61	//verifichiamo se il database esista già
     62	//https://medium.com/@Usurer/ef-core-check-if-db-exists-feafe6e36f4e
     63	//https://stackoverflow.com/questions/33911316/entity-framework-core-how-to-check-if-database-exists
     64	if (db.Database.GetService<IRelationalDatabaseCreator>().Exists())
     65	{
     66		WriteLineWithColor("Il database esiste già, vuoi ricrearlo da capo? Tutti i valori precedentemente inseriti verranno persi. [Si, No]", ConsoleColor.Red);
     67		bool dbErase = Console.ReadLine()?.StartsWith("si", StringComparison.CurrentCultureIgnoreCase) ?? false;
     68		if (dbErase)
     69		{
     70			//cancelliamo il database se esiste
     71			db.Database.EnsureDeleted();
     72			//ricreiamo il database a partire dal model (senza dati --> tabelle vuote)
     73			db.Database.EnsureCreated();
     74			//inseriamo i dati nelle tabelle
     75			PopulateDb(db);
     76		}
     77	}
     78	else //il database non esiste
     79	{
     80		//ricreiamo il database a partire dal model (senza dati --> tabelle vuote)
     81		db.Database.EnsureCreated();
     82		//popoliamo il database
     83		PopulateDb(db);
     84	}
     85
     86	static void PopulateDb(FattureClientiContext db)
     87	{
     88		//Creazione dei Clienti - gli id vengono generati automaticamente come campi auto-incremento quando si effettua l'inserimento, tuttavia
     89		//è bene inserire esplicitamente l'id degli oggetti quando si procede all'inserimento massivo gli elementi mediante un foreach perché
     90		//EF core potrebbe inserire nel database gli oggetti in un ordine diverso rispetto a quello del foreach
     91		// https://stackoverflow.com/a/54692592
     92		// https://stackoverflow.com/questions/11521057/insertion-order-of-multiple-records-in-entity-framework/
     93		List<Cliente> listaClienti =
     94		[
     95			new (){ClienteId=1, RagioneSociale= "Cliente 1", PartitaIVA= "1111111111", Citta = "Napoli", Via="Via dei Mille", Civico= "23", CAP="80100"},
     96			new (){ClienteId=2, RagioneSociale= "Cliente 2", PartitaIVA= "1111111112", Citta = "Roma", Via="Via dei Fori Imperiali", Civico= "1", CAP="00100"},
     97			new (){ClienteId=3, RagioneSociale= "Cliente 3", PartitaIVA= "1111111113", Citta = "Firenze", Via="Via Raffaello", Civico= "10", CAP="50100"}
     98		];
     99
    100		//Creazione delle Fatture
    101		List<Fattura> listaFatture =
    102		[
    103			new (){FatturaId=1, Data= DateTime.Now.Date, Importo = 1200.45m, ClienteId = 1},
    104			new (){FatturaId=2, Data= DateTime.Now.AddDays(-5).Date, Importo = 3200.65m, ClienteId = 1},
    105			new (){FatturaId=3, Data= new DateTime(2019,10,20).Date, Importo = 5200.45m, ClienteId = 1},
    106			new (){FatturaId=4, Data= DateTime.Now.Date, Importo = 5200.45m, ClienteId = 2},
    107			new (){FatturaId=5, Data= new DateTime(2019,08,20).Date, Importo = 7200.45m, ClienteId = 2}
    108		];
    109		Console.WriteLine("Inseriamo i clienti nel database");
    110		listaClienti.ForEach(c => db.Add(c));
    111		db.SaveChanges();
    112		Console.WriteLine("Inseriamo le fatture nel database");
    113		listaFatture.ForEach(f => db.Add(f));
    114		db.SaveChanges();
    115	}
    116}
    117
    118static void LetturaDb()
    119{
    120	//recuperiamo i dati dal database
    121	using FattureClientiContext db = new();
    122	//Nel caso in cui il database fosse stato eliminato ricreiamo un nuovo database vuoto a partire dal Model
    123	db.Database.EnsureCreated();
    124	//Il codice seguente è scritto in modo che se anche non ci fossero dati nelle tabelle non verrebbero sollevate eccezioni
    125	Console.WriteLine("Recuperiamo i dati dal database - senza alcuna elaborazione");
    126	List<Cliente> listaClienti = [.. db.Clienti];
    127	List<Fattura> listaFatture = [.. db.Fatture];
    128	Console.WriteLine("Stampa dei clienti");
    129	listaClienti.ForEach(Console.WriteLine);
    130	Console.WriteLine("Stampa delle fatture");
    131	listaFatture.ForEach(Console.WriteLine);
    132
    133	Console.WriteLine("Recuperiamo i dati dal database - uso di WHERE");
    134	Console.WriteLine("Recuperiamo i dati dal database - trovare le fatture fatte da almeno tre giorni");
    135	db.Fatture.Where(f => f.Data < DateTime.Now.AddDays(-2)).ToList().ForEach(f => Console.WriteLine(f));
    136	
    137	Console.WriteLine("Recuperiamo i dati dal database - trovare l'importo complessivo delle fatture fatte da almeno tre giorni ");
    138	Console.WriteLine($"Importo complessivo: {db.Fatture.Where(f => f.Data < DateTime.Now.AddDays(-2)).Sum(f => (double)f.Importo):C2}");
    139	
    140	Console.WriteLine("Recuperiamo i dati dal database - trovare l'importo medio delle fatture fatte da almeno tre giorni ");
    141	//troviamo prima di tutto quante sono le fatture
    142	static bool searchPredicate(Fattura f) => f.Data < DateTime.Now.AddDays(-2);
    143	var count = db.Fatture.Where(searchPredicate).Count();
    144	
    145	//se non ci sono elementi nella collection i metodi Average, Max e Min sollevano l'eccezione InvalidOperationException
    146	if (count > 0)
    147	{
    148		Console.WriteLine($"Importo medio: {db.Fatture.Where(searchPredicate).Average(f => (double)f.Importo):C2}");
    149		Console.WriteLine("Recuperiamo i dati dal database - trovare l'importo massimo delle fatture fatte da almeno tre giorni ");
    150		Console.WriteLine($"Importo massimo: {db.Fatture.Where(searchPredicate).Max(f => (double)f.Importo):C2}");
    151		Console.WriteLine("Recuperiamo i dati dal database - trovare l'importo minimo delle fatture fatte da almeno tre giorni ");
    152		Console.WriteLine($"Importo minimo: {db.Fatture.Where(searchPredicate).Min(f => (double)f.Importo):C2}");
    153		Console.WriteLine("Recuperiamo i dati dal database - trovare il numero delle fatture fatte da almeno tre giorni ");
    154		Console.WriteLine("Numero fatture: " + count);
    155	}
    156	Console.WriteLine("Recuperiamo i dati dal database - uso di WHERE e JOIN");
    157	Console.WriteLine("trovare il nome e l'indirizzo dei clienti che hanno speso più di 5000 EUR");
    158	var clientiConSpesa5000Plus = 
    159	db.Fatture
    160	.Where(f => f.Importo > 5000)
    161	.Join(db.Clienti,
    162		f => f.ClienteId,
    163		c => c.ClienteId,
    164		(f, c) => new { NumeroFattura = f.FatturaId, DataFattura = f.Data, NomeCliente = c.RagioneSociale, Indirizzo = c.Via + " N." + c.Civico + " " + c.CAP + " " + c.Citta });
    165	clientiConSpesa5000Plus.ToList().ForEach(Console.WriteLine);
    166
    167	//altro modo - uso delle navigation properties
    168	Console.WriteLine("Recuperiamo i dati dal database - Uso di Navigation Property per ottenere i dati dei clienti a partire dalle fatture");
    169	var clientiConSpesa5000Plus2 = 
    170	db.Fatture
    171	.Where(f => f.Importo > 5000)
    172	.Select(f => new
    173		{
    174			NumeroFattura = f.FatturaId,
    175			DataFattura = f.Data,
    176			NomeCliente = f.Cliente.RagioneSociale,
    177			Indirizzo = f.Cliente.Via + " N." + f.Cliente.Civico + " " + f.Cliente.CAP + " " + f.Cliente.Citta
    178		});
    179	clientiConSpesa5000Plus2.ToList().ForEach(Console.WriteLine);
    180}
    181
    182static void ModificaFattura()
    183{
    184	using FattureClientiContext db = new();
    185	//la modifica ha senso solo se il database esiste e ha almeno un paio di fatture inserite
    186	//Nel caso in cui il database fosse stato eliminato ricreiamo un nuovo database vuoto a partire dal Model
    187	db.Database.EnsureCreated();
    188	//Il codice seguente è scritto in modo che se anche non ci fossero dati nelle tabelle non verrebbero sollevate eccezioni
    189	var numeroFatture = db.Fatture.Count();
    190	if (numeroFatture > 2)
    191	{
    192		Console.WriteLine("\nModifichiamo i dati nel database");
    193		Console.WriteLine("Modifichiamo l'importo della prima fattura");
    194
    195		Console.WriteLine("\nle fatture prima della modifica sono:\n");
    196
    197		//stampiamo la lista delle fatture per vedere il risultato
    198		//se il database non esiste listaFatture sarà null.
    199
    200		List<Fattura> listaFatture = db.Fatture.ToList();
    201		Console.WriteLine("Stampa delle fatture");
    202		listaFatture.ForEach(f => Console.WriteLine(f));
    203
    204		//accediamo all'elemento da modificare
    205		Fattura? fattura = db.Fatture.Find(1);//trova l'entity con il valore della chiave (FatturaId) specificato
    206		if (fattura != null)//se ho trovato la fattura con FatturaId specificato
    207		{
    208			//modifichiamo la fattura
    209			fattura.Importo *= 1.2m;//incremento del 20% l'importo
    210			db.SaveChanges();//aggiorno il database
    211
    212			//stampiamo la lista delle fatture per vedere il risultato
    213			listaFatture = [.. db.Fatture];
    214			Console.WriteLine("Stampa delle fatture dopo la modifica");
    215			listaFatture.ForEach(Console.WriteLine);
    216		}
    217		Console.WriteLine("\nmodifichiamo anche la seconda fattura");
    218		//non è possibile usare ElementAt - https://stackoverflow.com/questions/5147767/why-is-the-query-operator-elementat-is-not-supported-in-linq-to-sql
    219		Fattura? secondaFattura = db.Fatture.Skip(1)?.First();
    220		if (secondaFattura != null)
    221		{
    222			secondaFattura.Importo *= 1.3m;
    223			db.SaveChanges();
    224			//stampiamo la lista delle fatture per vedere il risultato
    225			listaFatture = [.. db.Fatture];
    226			Console.WriteLine("Stampa delle fatture");
    227			listaFatture.ForEach(Console.WriteLine);
    228		}
    229		Console.WriteLine("\nmodifichiamo anche l'ultima");
    230		//non è possibile usare ElementAt - https://stackoverflow.com/questions/5147767/why-is-the-query-operator-elementat-is-not-supported-in-linq-to-sql
    231		Fattura? ultimaFattura = db.Fatture.OrderBy(f => f.FatturaId).Last();
    232		if (ultimaFattura != null)
    233		{
    234			ultimaFattura.Importo *= 1.5m;
    235			db.SaveChanges();
    236			//stampiamo la lista delle fatture per vedere il risultato
    237			listaFatture = db.Fatture.ToList();
    238			Console.WriteLine("Stampa delle fatture");
    239			listaFatture.ForEach(f => Console.WriteLine(f));
    240		}
    241	}
    242	else
    243	{
    244		WriteLineWithColor("Non ci sono abbastanza Fatture nel database per eseguire la funzionalità richiesta", ConsoleColor.Red);
    245	}
    246}
    247static void CancellazioneFattura()
    248{
    249	using FattureClientiContext db = new();
    250	//Nel caso in cui il database fosse stato eliminato ricreiamo un nuovo database vuoto a partire dal Model
    251	db.Database.EnsureCreated();
    252	//Il codice seguente è scritto in modo che se anche non ci fossero dati nelle tabelle non verrebbero sollevate eccezioni
    253	Console.WriteLine("\nEliminiamo un dato dal database");
    254	Console.WriteLine("\nPrima della cancellazione le fatture sono:");
    255	List<Fattura> listaFatture = [.. db.Fatture];
    256	Console.WriteLine("Stampa delle fatture");
    257	listaFatture.ForEach(Console.WriteLine);
    258	Console.WriteLine("\nEliminiamo la terza fattura");
    259	//accediamo alla terza fattura - possiamo accedere mediante chiave, oppure in base ad un elenco da cui prendiamo la terza fattura
    260	//decidiamo di eliminare la fattura con FatturaId=3
    261	Fattura? fatturaDaEliminare = db.Fatture.Find(3);
    262	if (fatturaDaEliminare != null)//se abbiamo trovato la fattura con id =3
    263	{
    264		Console.WriteLine($"eliminiamo la fattura con id {fatturaDaEliminare.FatturaId}");
    265		db.Remove(fatturaDaEliminare);
    266		db.SaveChanges();
    267		//stampiamo nuovamente l'elenco delle fatture per verificare che la fattura con id specifico è stata eliminata
    268		listaFatture = [.. db.Fatture];
    269		Console.WriteLine("Stampa delle fatture");
    270		listaFatture.ForEach(Console.WriteLine);
    271	}
    272	else
    273	{
    274		WriteLineWithColor("Non è stato possibile eliminare la fattura indicata, perché non esiste", ConsoleColor.Red);
    275	}
    276}
    277
    278static void CancellazioneDb()
    279{
    280	using FattureClientiContext db = new();
    281	WriteLineWithColor("Attenzione tutto il database andrà eliminato! Questa operazione non può essere revocata.\nSei sicuro di voler procedere? [Si, No]", ConsoleColor.Red);
    282	bool dbErase = Console.ReadLine()?.StartsWith("si", StringComparison.CurrentCultureIgnoreCase) ?? false;
    283	if (dbErase)
    284	{
    285		if (db.Database.EnsureDeleted())
    286		{
    287			Console.WriteLine("Database cancellato correttamente");
    288		}
    289		else
    290		{
    291			Console.WriteLine("Il database non esisteva e non è stata fatta alcuna azione");
    292		}
    293	}
    294}
    295
    296//stampa a console con il colore di foreground selezionato e successivamente ripristina il colore precedente
    297static void WriteLineWithColor(string text, ConsoleColor consoleColor)
    298{
    299	ConsoleColor previousColor = Console.ForegroundColor;
    300	Console.ForegroundColor = consoleColor;
    301	Console.WriteLine(text);
    302	Console.ForegroundColor = previousColor;
    303}
    304enum ModalitàOperativa
    305{
    306	CreazioneDb,
    307	LetturaDb,
    308	ModificaFattura,
    309	CancellazioneFattura,
    310	CancellazioneDb,
    311	Nessuna
    312}