Salta ai contenuti

Esercizi

In questa sezione verranno mostrati alcuni esercizi completamente svolti relativamente alla creazione di un database mediante EF Core e all’esecuzione di query con il linguaggio LINQ, nella variante Fluent Method.

Ottieni il codice

  1. Creare un progetto per un’applicazione Console .Net Core, chiamata Romanzi che utilizzi il Framework EF Core per implementare il database (Romanzi.db) con le seguenti classi:

    Autore(AutoreId, Nome, Cognome, Nazionalità )

    Romanzo(RomanzoId, Titolo, AutoreId*, AnnoPubblicazione )

    Personaggio(PersonaggioId, Nome, RomanzoId*, Sesso, Ruolo )

    Dove l’attributo sottolineato rappresenta la Primary Key, mentre l’attributo con l’asterisco rappresenta una Foreign Key.
    Assumere che:

    • Nome, Cognome, Nazionalità nella la classe Autore siano di tipo string
    • Titolo in Romanzo sia di tipo string;
    • AnnoPubblicazione in Romanzo sia di tipo int
    • Nome, Sesso, Ruolo in Personaggio siano di tipo string

    Nota N1: creare un progetto Console denominato Romanzi da Visual Studio Code
    Nota N2: installare le librerie per l’utilizzo di EF Core con SQLite:

    Finestra del terminale
    dotnet add package Microsoft.EntityFrameworkCore.Design
    dotnet add package Microsoft.EntityFrameworkCore.Sqlite
  2. Creare la cartella Model con dentro le classi del modello dei dati

  3. Creare la cartella Data con al suo interno la classe RomanziContext
    Nota N3: la classe RomanziContext discende da DbContext ed espone una property per ciascuna tabella del database, come esemplificato di seguito:

    public DbSet<NomeClasse> NomeTabella { get; set; }

    Nota N4: la classe RomanziContext potrebbe, se richiesto, implementare i metodi:

    protected override void OnConfiguring(DbContextOptionsBuilder options);
    protected override void OnModelCreating(ModelBuilder modelBuilder);
  4. Effettuare la migration del database e la creazione fisica del file di database di SQLite:

    Finestra del terminale
    dotnet ef migrations add InitialCreate
    dotnet ef database update
  5. Creare un metodo static void PopulateDB() nella classe Program che inserisce:
    Almeno 5 autori di nazionalità compresa tra “Americana”, “Belga”, “Inglese”.
    Almeno 10 romanzi degli autori precedentemente inseriti
    Almeno 5 personaggi presenti nei romanzi precedentemente inseriti.

Q1: creare un metodo che prende in input la nazionalità e stampa gli autori che hanno la nazionalità specificata
Q2: creare un metodo che prende in input il nome e il cognome di un autore e stampa tutti i romanzi di quell’autore
Q3: creare un metodo che prende in input la nazionalità e stampa quanti romanzi di quella nazionalità sono presenti nel database
Q4: creare un metodo che per ogni nazionalità stampa quanti romanzi di autori di quella nazionalità sono presenti nel database
Q5: creare un metodo che stampa il nome dei personaggi presenti in romanzi di autori di una data nazionalità

Seguendo lo stesso procedimento visto negli esempi precedenti si creino i file relativi alle classi Romanzo, Autore, Personaggio nella cartella Model:

Program.cs
namespace Romanzi.Model;
public class Romanzo
{
public int RomanzoId { get; set; }
public string Titolo { get; set; } = null!;
public int AutoreId { get; set; }
public Autore Autore { get; set; } = null!;
public int? AnnoPubblicazione { get; set; }
public List<Personaggio> Personaggi { get; set; } = null!;
}
Program.cs
namespace Romanzi.Model;
public class Autore
{
public int AutoreId { get; set; }
public string Nome { get; set; } = null!;
public string Cognome { get; set; } = null!;
public string? Nazionalità { get; set; }
public List<Romanzo> Romanzi { get; set; } = null!;
}
Program.cs
namespace Romanzi.Model;
public class Personaggio
{
public int PersonaggioId { get; set; }
public string Nome { get; set; } = null!;
public int RomanzoId { get; set; }
public Romanzo Romanzo { get; set; } = null!;
public string? Sesso { get; set; }
public string? Ruolo { get; set; }
}

Si installino i pacchetti necessari per l’utilizzo di EF Core con SQLite:

Finestra del terminale
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Sqlite

Si crei la cartella Data con dentro il file RomanziContext.cs, relativo alla definizione della classe RomanziContext:

Program.cs
using Microsoft.EntityFrameworkCore;
using Romanzi.Model;
namespace Romanzi.Data;
public class RomanziContext : DbContext
{
public DbSet<Personaggio> Personaggi { get; set; } = null!;
public DbSet<Autore> Autori { get; set; } = null!;
public DbSet<Romanzo> Romanzi { get; set; } = null!;
public string DbPath { get; }
public RomanziContext()
{
var appDir = AppContext.BaseDirectory;
var path = Path.Combine(appDir, "../../../Romanzi.db");
DbPath = path;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite($"Data Source = {DbPath}");
}
}

Dal terminale di Visual Studio Code si eseguano i comandi per la generazione della migrazione e la creazione del database.

Finestra del terminale
dotnet ef migrations add InitialCreate
dotnet ef database update

La struttura del progetto dovrebbe essere simile a quella riportata nella figura seguente:

Struttura Progetto Romanzi

Si scriva nel file Program.cs il codice relativo a:

  • Popolamento del database
  • Gestione dell’input
  • Esecuzione delle query richieste dalla traccia

File Program.cs:

Program.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Romanzi.Data;
using Romanzi.Model;
InitTest();
WriteLineWithColor("\nEsecuzione di Q1: " +
"\ncreare un metodo che prende in input la nazionalità e stampa gli autori che hanno la nazionalità specificata" +
"\nCaso della nazionalità Americana\n", ConsoleColor.Cyan);
Q1("Americana");
WriteLineWithColor("\nEsecuzione di Q2: " +
"\ncreare un metodo che prende in input il nome e il cognome di un autore e stampa tutti i romanzi di quell’autore" +
"\nCaso di Ernest Hemingway\n",ConsoleColor.Cyan);
Q2("Ernest", "Hemingway");
WriteLineWithColor("\nEsecuzione di Q3: " +
"\ncreare un metodo che prende in input la nazionalità e stampa quanti romanzi di quella nazionalità sono presenti nel database" +
"\nCaso della nazionalità Americana\n", ConsoleColor.Cyan);
Q3("Americana");
WriteLineWithColor("\nEsecuzione di Q4: " +
"\ncreare un metodo che per ogni nazionalità stampa quanti romanzi di autori di quella nazionalità sono presenti nel database\n", ConsoleColor.Cyan);
Q4();
WriteLineWithColor("\nEsecuzione di Q5: " +
"\ncreare un metodo che stampa il nome dei personaggi presenti in romanzi di autori di una data nazionalità" +
"\nCaso della nazionalità Inglese\n", ConsoleColor.Cyan);
Q5("Inglese");
//Q1: creare un metodo che prende in input la nazionalità e
//stampa gli autori che hanno la nazionalità specificata
static void Q1(string nazionalità)
{
using var db = new RomanziContext();
Console.WriteLine($"Artisti di nazionalità {nazionalità}");
db.Autori
.Where(a => a.Nazionalità != null
&& a.Nazionalità.Equals(nazionalità))
.ToList()
.ForEach(a => Console.WriteLine($"{a.Nome} {a.Cognome}"));
}
//Q2: creare un metodo che prende in input il nome e il cognome di un autore
//e stampa tutti i romanzi di quell’autore
static void Q2(string nome, string cognome)
{
using var db = new RomanziContext();
//primo modo - uso di inner join
Console.WriteLine("primo modo - uso di Join");
db.Autori
.Where(a => a.Nome.Equals(nome)
&& a.Cognome.Equals(cognome))//filtriamo per nome e cognome
.Join(db.Romanzi,//Join con romanzi
a => a.AutoreId,
r => r.AutoreId,
(a, r) => new { a.Nome, a.Cognome, r.Titolo, r.AnnoPubblicazione })//prendiamo le colonne che interessano
.ToList()//restituiamo al client il risultato
.ForEach(t => Console.WriteLine(t));//processiamo il risultato
//secondo metodo - uso di navigation property a partire da Romanzi
Console.WriteLine("secondo modo - uso di navigation property a partire da Romanzi");
db.Romanzi
.Where(r => r.Autore.Nome.Equals(nome)
&& r.Autore.Cognome.Equals(cognome))
.ToList()//restituiamo al client il risultato
.ForEach(t => Console.WriteLine($"\t{t.Titolo} {t.AnnoPubblicazione}"));//processiamo il risultato
//terzo modo - uso di navigation property
Console.WriteLine("terzo modo - uso di navigation property a partire da Autori - più involuto");
db.Autori
.Where(a => a.Nome.Equals(nome)
&& a.Cognome.Equals(cognome))//filtriamo per nome e cognome
.Select(a => new { a, RomanziDiAutore = a.Romanzi })//usiamo la navigation property a.Romanzi
.ToList()//restituiamo al client il risultato
.ForEach(t =>
{ //processiamo il risultato
Console.WriteLine($"Autore = {t.a.Nome} {t.a.Cognome}");
if (t.RomanziDiAutore != null)
{
foreach (var libro in t.RomanziDiAutore)
{
Console.WriteLine($"\t{libro.Titolo} {libro.AnnoPubblicazione}");
}
}
});
//quarto modo - uso di navigation property e di Include
//👇👇👇
//ATTENZIONE! Le collection property e le navigation property non sono automaticamente caricate nell'app, quando si esegue una query.
//https://learn.microsoft.com/en-us/ef/core/querying/related-data/
//https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager
//👆👆👆
//In questo esempio se togliessimo l'include e commentassimo i due modi precedenti di eseguire la query
//vedremmo che il risultato non includerebbe i Romanzi
Console.WriteLine("quarto modo - uso di navigation property e di Include");
db.Autori
.Where(a => a.Nome.Equals(nome)
&& a.Cognome.Equals(cognome))//filtriamo per nome e cognome
.Include(a => a.Romanzi)//chiediamo al provider del database di caricare i romanzi tramite la navigation property
.ToList()//restituiamo al client il risultato
.ForEach(a =>
{ //processiamo il risultato
Console.WriteLine($"Autore = {a.Nome} {a.Cognome}");
if (a.Romanzi != null)
{
foreach (var libro in a.Romanzi)
{
Console.WriteLine($"\t{libro.Titolo} {libro.AnnoPubblicazione}");
}
}
});
}
//Q3: creare un metodo che prende in input la nazionalità e stampa quanti romanzi di quella nazionalità
//sono presenti nel database
static void Q3(string nazionalità)
{
using var db = new RomanziContext();
//primo modo - uso di join
var numeroRomanzi = db.Autori
.Where(a => a.Nazionalità != null
&& a.Nazionalità.Equals(nazionalità))//filtriamo per nazionalità
.Join(db.Romanzi,//Join con romanzi
a => a.AutoreId,
r => r.AutoreId,
(a, r) => new { r.RomanzoId })
.Count();
Console.WriteLine($"I romanzi di nazionalità {nazionalità} sono: {numeroRomanzi}");
//secondo modo - uso di navigation property
var numeroRomanzi2 = db.Romanzi
.Where(r => r.Autore.Nazionalità != null
&& r.Autore.Nazionalità.Equals(nazionalità))
.Count();
Console.WriteLine("Secondo metodo");
Console.WriteLine($"I romanzi di nazionalità {nazionalità} sono: {numeroRomanzi2}");
}
//Q4: creare un metodo che per ogni nazionalità stampa quanti romanzi di autori di quella
//nazionalità sono presenti nel database
static void Q4()
{
//primo modo - facciamo una join seguita da un raggruppamento
//In questo esempio è molto importante effettuare la Select dopo la GroupBy, altrimenti si ottiene un errore:
//la Select serve a completare l'esecuzione della query all'interno del database con il calcolo di eventuale
//funzione di gruppo. Solo successivamente, con il ToList si restituisce il risultato della GroupBy all'applicazione
Console.WriteLine("Primo metodo - uso di Join e di Group By");
using var db = new RomanziContext();
db.Autori
.Join(db.Romanzi,
a => a.AutoreId,
r => r.AutoreId,
(a, r) => new { Nazionalita = a.Nazionalità, r.RomanzoId })
.GroupBy(r => r.Nazionalita)//effettuiamo il raggruppamento
.Select(g => new { Nazionalità = g.Key, NumeroRomanzi = g.Count() })//calcoliamo la funzione di gruppo - Count in questo caso
.ToList() //restituiamo il risultato all'app
.ForEach(t => Console.WriteLine($"Nazionalità = {t.Nazionalità}; numero romanzi = {t.NumeroRomanzi}"));//processiamo il risultato
//secondo modo - uso di navigation properties
Console.WriteLine("\nSecondo metodo - uso di GroupBy con navigation properties");
db.Romanzi
.GroupBy(r => r.Autore.Nazionalità)//effettuo il raggruppamento
.Select(g => new { Nazionalità = g.Key, NumeroRomanzi = g.Count() })//calcoliamo la funzione di gruppo - Count in questo caso
.ToList()//restituiamo il risultato all'app
.ForEach(t => Console.WriteLine($"Nazionalità = {t.Nazionalità}; numero romanzi = {t.NumeroRomanzi}"));//processiamo il risultato
}
//Q5: creare un metodo che stampa il nome dei personaggi presenti in romanzi di autori di una data nazionalità
static void Q5(string nazionalità)
{
using var db = new RomanziContext();
//primo modo - uso di join di join
Console.WriteLine("Primo modo - uso di join");
db.Autori
.Where(a => a.Nazionalità != null
&& a.Nazionalità.Equals(nazionalità))
.Join(db.Romanzi,
a => a.AutoreId,
r => r.AutoreId,
(a, r) => new { r.RomanzoId })
.Join(db.Personaggi,
r => r.RomanzoId,
p => p.RomanzoId,
(r, p) => p)
.ToList()
.ForEach(p => Console.WriteLine($"{p.Nome} {p.Ruolo} {p.Sesso}"));
//secondo modo - uso di navigation property
Console.WriteLine("\nSecondo modo - uso di navigation property");
db.Personaggi
.Where(p => p.Romanzo.Autore.Nazionalità != null
&& p.Romanzo.Autore.Nazionalità.Equals(nazionalità))
.ToList()
.ForEach(p => Console.WriteLine($"{p.Nome} {p.Ruolo} {p.Sesso}"));
}
static void InitTest()
{
RomanziContext db = new();
//verifichiamo se il database esista già
//https://medium.com/@Usurer/ef-core-check-if-db-exists-feafe6e36f4e
//https://stackoverflow.com/questions/33911316/entity-framework-core-how-to-check-if-database-exists
if (db.Database.GetService<IRelationalDatabaseCreator>().Exists())
{
WriteLineWithColor("Il database esiste già, vuoi ricrearlo da capo? Tutti i valori precedentemente inseriti verranno persi. [Si, No]", ConsoleColor.Red);
bool dbErase = Console.ReadLine()?.StartsWith("si", StringComparison.CurrentCultureIgnoreCase) ?? false;
if (dbErase)
{
//cancelliamo il database se esiste
db.Database.EnsureDeleted();
//ricreiamo il database a partire dal model (senza dati --> tabelle vuote)
db.Database.EnsureCreated();
//inseriamo i dati nelle tabelle
PopulateDb(db);
Console.WriteLine("Database ricreato correttamente");
}
}
else //il database non esiste
{
//ricreiamo il database a partire dal model (senza dati --> tabelle vuote)
db.Database.EnsureCreated();
//popoliamo il database
PopulateDb(db);
Console.WriteLine("Database creato correttamente");
}
static void PopulateDb(RomanziContext db)
{
//Almeno 5 autori di nazionalità compresa tra "Americana", "Belga", "Inglese".
List<Autore> autori =
[
new (){AutoreId=1, Nome="Ernest",Cognome="Hemingway", Nazionalità="Americana"},//AutoreId=1
new (){AutoreId=2,Nome="Philip",Cognome="Roth", Nazionalità="Americana"},//AutoreId=2
new (){AutoreId=3,Nome="Thomas",Cognome="Owen", Nazionalità="Belga"},//AutoreId=3
new (){AutoreId=4,Nome="William",Cognome="Shakespeare", Nazionalità="Inglese"},//AutoreId=4
new (){AutoreId=5,Nome="Charles",Cognome="Dickens", Nazionalità="Inglese"},//AutoreId=5
];
autori.ForEach(a => db.Add(a));
db.SaveChanges();
//Almeno 10 romanzi degli autori precedentemente inseriti
List<Romanzo> romanzi =
[
new (){RomanzoId=1, Titolo="For Whom the Bell Tolls", AnnoPubblicazione=1940, AutoreId=1},//RomanzoId=1
new (){RomanzoId=2,Titolo="The Old Man and the Sea", AnnoPubblicazione=1952, AutoreId=1},
new (){RomanzoId=3,Titolo="A Farewell to Arms",AnnoPubblicazione=1929, AutoreId=1},
new (){RomanzoId=4,Titolo="Letting Go", AnnoPubblicazione=1962, AutoreId=2},
new (){RomanzoId=5,Titolo="When She Was Good", AnnoPubblicazione=1967, AutoreId=2},
new (){RomanzoId=6,Titolo="Destination Inconnue", AnnoPubblicazione=1942, AutoreId=3},
new (){RomanzoId=7,Titolo="Les Fruits de l'orage", AnnoPubblicazione=1984, AutoreId=3},
new (){RomanzoId=8,Titolo="Giulio Cesare", AnnoPubblicazione=1599, AutoreId=4},
new (){RomanzoId=9,Titolo="Otello", AnnoPubblicazione=1604, AutoreId=4},
new (){RomanzoId=10,Titolo="David Copperfield", AnnoPubblicazione=1849, AutoreId=5},
];
romanzi.ForEach(r => db.Add(r));
db.SaveChanges();
//Almeno 5 personaggi presenti nei romanzi precedentemente inseriti
List<Personaggio> personaggi =
[
new (){PersonaggioId=1, Nome="Desdemona", Ruolo="Protagonista", Sesso="Femmina", RomanzoId=9},//PersonaggioId=1
new (){PersonaggioId=2,Nome="Jago", Ruolo="Protagonista", Sesso="Maschio", RomanzoId=9},
new (){PersonaggioId=3,Nome="Robert", Ruolo="Protagonista", Sesso="Maschio", RomanzoId=1},
new (){PersonaggioId=4,Nome="Cesare", Ruolo="Protagonista", Sesso="Maschio", RomanzoId=8},
new (){PersonaggioId=5,Nome="David", Ruolo="Protagonista", Sesso="Maschio", RomanzoId=10}
];
personaggi.ForEach(p => db.Add(p));
db.SaveChanges();
}
}
//stampa a console con il colore di foreground selezionato e successivamente ripristina il colore precedente
static void WriteLineWithColor(string text, ConsoleColor consoleColor)
{
ConsoleColor previousColor = Console.ForegroundColor;
Console.ForegroundColor = consoleColor;
Console.WriteLine(text);
Console.ForegroundColor = previousColor;
}

Ottieni il codice

  1. Creare un progetto per un’applicazione Console .Net Core, chiamata DbUtilizziPC che utilizzi il Framework EF Core per implementare il database (DbUtilizziPC.db) con le seguenti classi:

    Classe(Id, Nome, Aula )
    Studente(Id, Nome, Cognome, ClasseId* )
    Utilizza(StudenteId*,ComputerId*,DataOraInizioUtilizzo, DataOraFineUtilizzo )
    Computer(Id, Modello, Collocazione )

    Dove l’attributo sottolineato rappresenta la Primary Key, mentre l’attributo con l’asterisco rappresenta una Foreign Key.
    Assumere che:

    • Gli Id siano di tipo intero ad auto-incremento
    • Nome, Aula nell’entità Classe siano di tipo string
    • Nome in Cognome nell’entità Studente siano di tipo string;
    • DataOraInizioUtilizzo e DataOraFineUtilizzo nell’entità Utilizza siano di tipo DateTime
    • Modello e Collocazione nell’entità Computer siano di tipo string

    Nota N1: creare un progetto Console denominato DbUtilizziPC da Visual Studio Code Nota N2: installare le librerie per l’utilizzo di EF Core con SQLite:

    Finestra del terminale
    dotnet add package Microsoft.EntityFrameworkCore.Design
    dotnet add package Microsoft.EntityFrameworkCore.Sqlite
  2. Creare la cartella Model con dentro le classi del modello dei dati

  3. Creare la cartella Data con al suo interno la classe UtilizziPCContext
    Nota N3: la classe UtilizziPCContext discende da DbContext ed espone una property per ciascuna tabella del database, come esemplificato di seguito:

    public DbSet<NomeClasse> NomeTabella { get; set; }

    Nota N4: la classe UtilizziPCContext potrebbe, se richiesto, implementare i metodi:

    protected override void OnConfiguring(DbContextOptionsBuilder options);
    protected override void OnModelCreating(ModelBuilder modelBuilder);
  4. Effettuare la migration del database e la creazione fisica del file di database di SQLite:

    Finestra del terminale
    dotnet ef migrations add InitialCreate
    dotnet ef database update
  5. Creare un metodo static void PopulateDB() nella classe Program che inserisce:

  • Almeno 6 classi, con le relative aule
  • Almeno 10 studenti distribuiti su più classi (non necessariamente devono esserci studenti di ogni classe)
  • Almeno 20 computer
  • Almeno una quindicina di utilizzi

La creazione del Model e del DBContext procede in maniera del tutto analoga a quanto già visto negli esempi precedenti.
Per il Model i file sono:

Classe.cs
using Microsoft.EntityFrameworkCore;
namespace DbUtilizziPC.Model;
//https://learn.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations#index-uniqueness
[Index(nameof(Nome), IsUnique = true)]
public class Classe
{
public int Id { get; set; }
public string Nome { get; set; } = null!;
public string Aula { get; set; } = null!;
//navigation property
public ICollection<Studente> Studenti { get; set; } = null!;
public override string ToString()
{
return $"{{{nameof(Id)} = {Id}, {nameof(Nome)} = {Nome}, {nameof(Aula)} = {Aula}}}";
}
}
Studente.cs
namespace DbUtilizziPC.Model;
public class Studente
{
public int Id { get; set; }
public string Nome { get; set; } = null!;
public string Cognome { get; set; } = null!;
//Foreign Key su Classe
public int ClasseId { get; set; }
//Navigation Property su Classe
public Classe Classe { get; set; } = null !;
//navigation property per Molti a Molti
public List<Utilizza> Utilizzi { get; } = null!;
//Skip Navigation Property
public List<Computer> Computers { get; } = null!;
public override string ToString()
{
return $"{{{nameof(Id)} = {Id}, {nameof(Nome)} = {Nome}, {nameof(Cognome)} = {Cognome}, {nameof(Classe)} = {Classe}}}";
}
}
Computer.cs
namespace DbUtilizziPC.Model;
public class Computer
{
public int Id { get; set; }
public string Modello { get; set; } = null!;
public string Collocazione { get; set; } = null!;
public List<Utilizza> Utilizzi { get; } = null!;
//Skip Navigation Property
public List<Studente> Studenti { get; } = null!;
public override string ToString()
{
return $"{{{nameof(Id)} = {Id}, {nameof(Modello)} = {Modello}, {nameof(Collocazione)} = {Collocazione}}}";
}
}
Computer.cs
namespace DbUtilizziPC.Model;
public class Computer
{
public int Id { get; set; }
public string Modello { get; set; } = null!;
public string Collocazione { get; set; } = null!;
public List<Utilizza> Utilizzi { get; } = null!;
//Skip Navigation Property
public List<Studente> Studenti { get; } = null!;
public override string ToString()
{
return $"{{{nameof(Id)} = {Id}, {nameof(Modello)} = {Modello}, {nameof(Collocazione)} = {Collocazione}}}";
}
}
Utilizza.cs
using Microsoft.EntityFrameworkCore;
namespace DbUtilizziPC.Model;
[PrimaryKey(nameof(StudenteId), nameof(ComputerId), nameof(DataOraInizioUtilizzo))]
public class Utilizza
{
public int StudenteId { get; set; }
public Studente Studente { get; set; } = null!;
public int ComputerId { get; set; }
public Computer Computer { get; set; } = null!;
public DateTime DataOraInizioUtilizzo { get; set; }
public DateTime? DataOraFineUtilizzo { get; set; }
public override string ToString()
{
return $"{{{nameof(StudenteId)} = {StudenteId}, {nameof(ComputerId)} = {ComputerId}," +
$" {nameof(DataOraInizioUtilizzo)} = {DataOraInizioUtilizzo}," +
$" {nameof(DataOraFineUtilizzo)} = {DataOraFineUtilizzo}}}";
}
}

Il DbContext è definito attraverso il file UtilizziPCContext.cs relativo alla definizione della classe UtilizziPCContext:

UtilizziPCContext.cs
using DbUtilizziPC.Model;
using Microsoft.EntityFrameworkCore;
namespace DbUtilizziPC.Data;
public class UtilizziPCContext:DbContext
{
public DbSet<Classe> Classi { get; set; } = null!;
public DbSet<Studente> Studenti { get; set; } = null!;
public DbSet<Computer> Computers { get; set; } = null!;
public DbSet<Utilizza> Utilizzi { get; set; } = null!;
public string DbPath { get; }
public UtilizziPCContext()
{
var cartellaApp = AppContext.BaseDirectory;
DbPath = Path.Combine(cartellaApp, "../../../DbUtilizziPC.db");
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite($"Data Source = {DbPath}");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Studente>()
.HasMany(s => s.Computers)
.WithMany(c => c.Studenti)
.UsingEntity<Utilizza>(
//left => left.HasOne(u => u.Computer).WithMany(c => c.Utilizzi).HasForeignKey(u => u.ComputerId),
//right => right.HasOne(u => u.Studente).WithMany(s => s.Utilizzi).HasForeignKey(u => u.StudenteId),
//k =>k.HasKey( u => new {u.ComputerId, u.StudenteId, u.DataOraInizioUtilizzo })
);
}
}

Dopo aver fatto la migration e la creazione del database, si scriva il codice delle query seguenti, espresse mediante metodi:
Q1(string classe): Stampa a Console il numero di alunni della classe in input; ad esempio, stampa il numero di alunni della 4IA
Q2(): Stampa a Console il numero di alunni per ogni classe
Q3(): Stampa gli studenti che non hanno ancora restituito i computer (sono quelli collegati a Utilizza con DataOraFineUtilizzo pari a null)
Q4(string classe): Stampa l’elenco dei computer che sono stati utilizzati dagli studenti della classe specificata in input. Ad esempio, stampare l’elenco dei computer utilizzati dalla 4IA. Non mostrare ripetizioni nella stampa.
Q5(int computerId): Dato un computer (di cui si conosce l’Id) riporta l’elenco degli studenti che lo hanno usato negli ultimi 30 giorni, con l’indicazione della DataOraInizioUtilizzo, ordinando i risultati per classe e, a parità di classe, per data (mostrando prima le date più recenti)
Q6(): Stampa per ogni classe quanti utilizzi di computer sono stati fatti negli ultimi 30 giorni.
Q7(): Stampa la classe che ha utilizzato maggiormente i computer (quella con il maggior numero di utilizzi) negli ultimi 30 giorni.

Program.cs
using DbUtilizziPC.Data;
using DbUtilizziPC.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using System.Text;
namespace DbUtilizziPC;
internal class Program
{
enum ModalitàOperativa
{
CreazioneDb,
Q1,
Q2,
Q3,
Q4,
Q5,
Q6,
Q7,
Nessuna,
CancellazioneDb
}
static void Main(string[] args)
{
Console.OutputEncoding = Encoding.UTF8;
Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("it-IT");
//colore di default della console
Console.ForegroundColor = ConsoleColor.Cyan;
//gestione del menu
bool uscitaDalProgramma;
do
{
//gestione della scelta
bool correctInput;
do
{
Console.Write("Inserire la modalità operativa [");
foreach(var elem in Enum.GetNames(typeof(ModalitàOperativa)))
{
Console.Write(elem+", ");
}
Console.WriteLine("]");
correctInput = Enum.TryParse(Console.ReadLine(), true, out ModalitàOperativa modalitàOperativa);
if (correctInput)
{
switch (modalitàOperativa)
{
case ModalitàOperativa.CreazioneDb:
//using effettua il Dispose del context automaticamente
using(UtilizziPCContext db = new ())
{
CreazioneDb(db);
}
break;
case ModalitàOperativa.Q1:
Console.WriteLine("Q1: Contare gli alunni di una classe");
Console.WriteLine("Inserire la classe");
string? classe = Console.ReadLine()?.ToUpper();
if(classe != null)
{
using UtilizziPCContext db = new();
Q1(classe, db);
}
break;
case ModalitàOperativa.Q2:
Console.WriteLine("Q2: Riportare il numero di alunni per ogni classe");
using (UtilizziPCContext db = new())
{
Q2(db);
}
break;
case ModalitàOperativa.Q3:
Console.WriteLine("Q3: Stampa gli studenti che non hanno ancora restituito i computer (sono quelli collegati a Utilizza con DataOraFineUtilizzo pari a null)");
using (UtilizziPCContext db = new())
{
Q3(db);
}
break;
case ModalitàOperativa.Q4:
Console.WriteLine("Q4: Stampa l’elenco dei computer che sono stati utilizzati dagli studenti della classe specificata in input. ");
Console.WriteLine("Inserire la classe");
classe = Console.ReadLine()?.ToUpper();
if (classe != null)
{
using UtilizziPCContext db = new();
Q4(classe, db);
}
break;
case ModalitàOperativa.Q5:
Console.WriteLine("Q5: Dato un computer (di cui si conosce l’Id) riporta l’elenco degli studenti che lo hanno usato negli ultimi 30 giorni, con l'indicazione della DataOraInizioUtilizzo," +
"ordinando i risultati per classe e, a parità di classe, per data (mostrando prima le date più recenti)");
Console.WriteLine("Inserire l'id del computer");
bool correctId = int.TryParse(Console.ReadLine(), out int computerId) && computerId>=0;
if (correctId)
{
using UtilizziPCContext db = new();
Q5(computerId, db);
}
break;
case ModalitàOperativa.Q6:
Console.WriteLine("Q6: Stampa per ogni classe quanti utilizzi di computer sono stati fatti negli ultimi 30 giorni.");
using (UtilizziPCContext db = new())
{
Q6(db);
}
break;
case ModalitàOperativa.Q7:
Console.WriteLine("Q7: Stampa le classi che hanno utilizzato maggiormente i computer (quelle con il maggior numero di utilizzi) " +
"negli ultimi 30 giorni");
using (UtilizziPCContext db = new())
{
Q7(db);
}
break;
case ModalitàOperativa.CancellazioneDb:
using (UtilizziPCContext db = new())
{
CancellazioneDb(db);
}
break;
default:
WriteLineWithColor("Non è stata impostata nessuna modalità operativa", ConsoleColor.Yellow);
break;
}
}
if (!correctInput)
{
Console.Clear();
WriteLineWithColor("Il valore inserito non corrisponde a nessuna opzione valida.\nI valori ammessi sono: [Creazione, Lettura, Modifica, Cancellazione, Nessuna]", ConsoleColor.Red);
}
} while (!correctInput);
Console.WriteLine("Uscire dal programma?[Si, No]");
uscitaDalProgramma = Console.ReadLine()?.ToLower().StartsWith("si") ?? false;
Console.Clear();
} while (!uscitaDalProgramma);
}
static void CreazioneDb(UtilizziPCContext db)
{
//verifichiamo se il database esista già
//https://medium.com/@Usurer/ef-core-check-if-db-exists-feafe6e36f4e
//https://stackoverflow.com/questions/33911316/entity-framework-core-how-to-check-if-database-exists
if (db.Database.GetService<IRelationalDatabaseCreator>().Exists())
{
WriteLineWithColor("Il database esiste già, vuoi ricrearlo da capo? Tutti i valori precedentemente inseriti verranno persi. [Si, No]", ConsoleColor.Red);
bool dbErase = Console.ReadLine()?.ToLower().StartsWith("si") ?? false;
if (dbErase)
{
//cancelliamo il database se esiste
db.Database.EnsureDeleted();
//ricreiamo il database a partire dal model (senza dati --> tabelle vuote)
db.Database.EnsureCreated();
//inseriamo i dati nelle tabelle
PopulateDb(db);
}
}
else //il database non esiste
{
//ricreiamo il database a partire dal model (senza dati --> tabelle vuote)
db.Database.EnsureCreated();
//popoliamo il database
PopulateDb(db);
}
static void PopulateDb(UtilizziPCContext db)
{
//Creazione dei Clienti - gli id vengono generati automaticamente come campi auto-incremento quando si effettua l'inserimento, tuttavia
//è bene inserire esplicitamente l'id degli oggetti quando si procede all'inserimento massivo gli elementi mediante un foreach perché
//EF core potrebbe inserire nel database gli oggetti in un ordine diverso rispetto a quello del foreach
// https://stackoverflow.com/a/54692592
// https://stackoverflow.com/questions/11521057/insertion-order-of-multiple-records-in-entity-framework/
List<Classe> classi =
[
new (){Id =1, Nome="3IA", Aula="Est 1"},
new (){Id =2,Nome="4IA", Aula="A32"},
new (){Id =3,Nome="5IA", Aula="A31"},
new (){Id =4,Nome="3IB", Aula="Est 2"},
new (){Id =5,Nome="4IB", Aula="A30"},
new (){Id =6,Nome="5IB", Aula="A32"},
];
List<Studente> studenti =
[
new (){Id = 1, Nome = "Mario", Cognome = "Rossi", ClasseId =1 },
new (){Id = 2, Nome = "Giovanni", Cognome = "Verdi", ClasseId =1 },
new (){Id = 3, Nome = "Piero", Cognome = "Angela", ClasseId = 1 },
new (){Id = 4, Nome = "Leonardo", Cognome = "Da Vinci", ClasseId = 1 },
new (){Id = 50, Nome = "Cristoforo", Cognome = "Colombo", ClasseId=2 },
new (){Id = 51, Nome = "Piero", Cognome = "Della Francesca", ClasseId=2 },
new (){Id = 82, Nome = "Alessandro", Cognome = "Manzoni", ClasseId=4 },
new (){Id = 83, Nome = "Giuseppe", Cognome = "Parini", ClasseId=4 },
new (){Id = 102, Nome = "Giuseppe", Cognome = "Ungaretti", ClasseId=3 },
new (){Id = 103, Nome = "Luigi", Cognome = "Pirandello", ClasseId=3 },
new (){Id = 131, Nome = "Enrico", Cognome = "Fermi", ClasseId=6 },
new (){Id = 132, Nome = "Sandro", Cognome = "Pertini", ClasseId=6 },
];
List<Computer> computers =
[
new (){Id = 1, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D1-D5"},
new (){Id = 2, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D1-D5"},
new (){Id = 3, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D1-D5"},
new (){Id = 4, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D1-D5"},
new (){Id = 5, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D1-D5"},
new (){Id = 6, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D6-D10"},
new (){Id = 7, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D6-D10"},
new (){Id = 8, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D6-D10"},
new (){Id = 9, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D6-D10"},
new (){Id = 10, Modello="Hp 19 inc. 2019", Collocazione = "Bunker-D6-D10"},
new (){Id = 20, Modello="Lenovo i5 2020", Collocazione = "Bunker-D20-D25"},
new (){Id = 21, Modello="Lenovo i5 2020", Collocazione = "Bunker-D20-D25"},
new (){Id = 22, Modello="Lenovo i5 2020", Collocazione = "Bunker-D20-D25"},
new (){Id = 23, Modello="Lenovo i5 2020", Collocazione = "Bunker-D20-D25"},
new (){Id = 24, Modello="Lenovo i5 2020", Collocazione = "Bunker-D20-D25"},
new (){Id = 61, Modello="Lenovo i5 2021", Collocazione = "Carrello-Mobile-S1"},
new (){Id = 62, Modello="Lenovo i5 2021", Collocazione = "Carrello-Mobile-S2"},
new (){Id = 63, Modello="Lenovo i5 2021", Collocazione = "Carrello-Mobile-S3"},
new (){Id = 64, Modello="Lenovo i5 2021", Collocazione = "Carrello-Mobile-S4"},
new (){Id = 65, Modello="Lenovo i5 2021", Collocazione = "Carrello-Mobile-S5"},
];
List<Utilizza> utilizzi = new()
{
new (){ComputerId = 61,StudenteId=1,
DataOraInizioUtilizzo = DateTime.Now.Add(- new TimeSpan(1,12,0)),
DataOraFineUtilizzo = DateTime.Now},
new (){ComputerId = 61,StudenteId=1,
DataOraInizioUtilizzo = DateTime.Now.Add(- new TimeSpan(1,1,12,0)),
DataOraFineUtilizzo = DateTime.Now.Add(- new TimeSpan(1,0,0,0))},
new (){ComputerId = 61,StudenteId=3,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-2).AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddDays(-2).AddHours(12)},
new (){ComputerId = 61,StudenteId=82,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-1).AddHours(12),
DataOraFineUtilizzo = DateTime.Today.AddDays(-1).AddHours(13) },
new (){ComputerId = 61,StudenteId=1,
DataOraInizioUtilizzo = DateTime.Today.AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddHours(12) },
new (){ComputerId = 62,StudenteId=2,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-2).AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddDays(-2).AddHours(12) },
new (){ComputerId = 62,StudenteId=2,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-1).AddHours(12),
DataOraFineUtilizzo = DateTime.Today.AddDays(-1).AddHours(13) },
new (){ComputerId = 62,StudenteId=4,
DataOraInizioUtilizzo = DateTime.Today.AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddHours(11) },
new (){ComputerId = 1,StudenteId=50,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-2).AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddDays(-2).AddHours(12) },
new (){ComputerId = 1,StudenteId=103,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-1).AddHours(12),
DataOraFineUtilizzo = DateTime.Today.AddDays(-1).AddHours(13) },
new (){ComputerId = 1,StudenteId=50,
DataOraInizioUtilizzo = DateTime.Today.AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddHours(12) },
new (){ComputerId = 2,StudenteId=51,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-1).AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddDays(-1).AddHours(12) },
new (){ComputerId = 2,StudenteId=51,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-1).AddHours(12),
DataOraFineUtilizzo = DateTime.Today.AddDays(-1).AddHours(13) },
new (){ComputerId = 2,StudenteId=103,
DataOraInizioUtilizzo = DateTime.Today.AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddHours(12) },
new (){ComputerId = 3,StudenteId=82,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-2).AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddDays(-2).AddHours(12) },
new (){ComputerId = 3,StudenteId=82,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-1).AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddDays(-1).AddHours(13) },
new (){ComputerId = 3,StudenteId=83,
DataOraInizioUtilizzo = DateTime.Today.AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddHours(12) },
new (){ComputerId = 20,StudenteId=102,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-2).AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddDays(-2).AddHours(12) },
new (){ComputerId = 20,StudenteId=103,
DataOraInizioUtilizzo = DateTime.Today.AddDays(-1).AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddDays(-1).AddHours(12) },
new (){ComputerId = 20,StudenteId=103,
DataOraInizioUtilizzo = DateTime.Today.AddHours(11),
DataOraFineUtilizzo = DateTime.Today.AddHours(12) },
new (){ComputerId = 64,StudenteId=131,
DataOraInizioUtilizzo = DateTime.Now.Add(- new TimeSpan(0,12,0)),
DataOraFineUtilizzo = null},
new (){ComputerId = 65,StudenteId=132,
DataOraInizioUtilizzo = DateTime.Now.Add(- new TimeSpan(1,12,0)),
DataOraFineUtilizzo = null},
};
Console.WriteLine("Inseriamo le classi nel database");
classi.ForEach(c => db.Add(c));
db.SaveChanges();
Console.WriteLine("Inseriamo gli studenti nel database");
studenti.ForEach(s => db.Add(s));
db.SaveChanges();
Console.WriteLine("Inseriamo i computers nel database");
computers.ForEach(c => db.Add(c));
db.SaveChanges();
Console.WriteLine("Inseriamo gli utilizzi nel database");
utilizzi.ForEach(u => db.Add(u));
db.SaveChanges();
}
}
//Q1: Contare gli alunni di una classe
private static void Q1(string classe, UtilizziPCContext db)
{
//esiste al più una sola classe con un dato nome
Classe? laClasse = db.Classi.Where(c => c.Nome==classe).FirstOrDefault();
if (laClasse != null)
{
int numeroStudentiDellaClasse = db.Studenti.Where(s => s.ClasseId == laClasse.Id).Count();
Console.WriteLine($"Il numero di studenti della classe {classe} è {numeroStudentiDellaClasse} ");
}
else
{
Console.WriteLine("Il nome fornito non corrisponde a nessuna classe");
}
}
//Q2: Riportare il numero di alunni per ogni classe
private static void Q2(UtilizziPCContext db)
{
//raggruppiamo gli studenti per classe e poi contiamo
db.Studenti
.GroupBy(s => s.Classe) //Classe è la navigation Property
.Select(g => new { g.Key, NumeroStudenti = g.Count() })//Key è un oggetto di tipo Classe
.ToList()
.ForEach(g => Console.WriteLine($"Classe = {g.Key.Nome}, Numero Alunni = {g.NumeroStudenti}"));
}
//Q3: Stampa gli studenti che non hanno ancora restituito i computer (sono quelli collegati a Utilizza con DataOraFineUtilizzo pari a null)
private static void Q3(UtilizziPCContext db)
{
Console.WriteLine("Versione con Navigation Property");
db.Utilizzi
.Where(u => u.DataOraFineUtilizzo == null)
//qui bisogna esplicitamente indicare ad EF Core di caricare anche la Classe di uno Studente
.Include(u => u.Studente.Classe)
.Select(u => u.Studente)
.ToList()
.ForEach(Console.WriteLine);
Console.WriteLine("Versione con Join");
db.Utilizzi
.Where(u => u.DataOraFineUtilizzo == null)
.Join(db.Studenti,
u => u.StudenteId,
s => s.Id,
(u, s) => s)
.Include(s => s.Classe)
.ToList()
.ForEach(Console.WriteLine);
}
//Q4: Stampa l’elenco dei computer che sono stati utilizzati dagli studenti della classe specificata in input.
private static void Q4(string classe, UtilizziPCContext db)
{
Console.WriteLine("Versione con Skip Navigation Property");
db.Studenti
.Where(s => s.Classe.Nome == classe)
//in questo caso ad ogni studente è associata una lista di computer.
//Per restituire una sola lista e non una lista di liste usare SelectMany
.SelectMany(s => s.Computers)
.Distinct()
.ToList()
.ForEach(Console.WriteLine);
Console.WriteLine("Versione con Join");
db.Studenti
.Where(s => s.Classe.Nome == classe)
.Join(db.Utilizzi,
s => s.Id,
u => u.StudenteId,
(s, u) => new { u.ComputerId })
.Join(db.Computers,
u => u.ComputerId,
c => c.Id,
(u, c) => c)
.Distinct()
.ToList()
.ForEach(Console.WriteLine);
}
//Q5: Dato un computer (di cui si conosce l’Id) riporta l’elenco degli studenti che lo hanno usato negli ultimi 30 giorni,
//con l'indicazione della DataOraInizioUtilizzo,ordinando i risultati per classe e, a parità di classe, per data
//(mostrando prima le date più recenti)
private static void Q5(int computerId, UtilizziPCContext db)
{
Console.WriteLine("Versione con Navigation Property");
db.Utilizzi
.Where(u => u.ComputerId == computerId && u.DataOraInizioUtilizzo >= DateTime.Now.AddDays(-30))
.Include(u => u.Studente)
.ThenInclude(s => s.Classe)
.OrderBy(u => u.Studente.Classe.Nome)
.ThenByDescending(u => u.DataOraInizioUtilizzo)
.ToList()
.ForEach(u => Console.WriteLine($"Studente = {u.Studente}, Data utilizzo computer = {u.DataOraInizioUtilizzo}"));
Console.WriteLine("Versione con Skip Navigation - in questo caso vogliamo solo sapere quali sono gli studenti che hanno usato il computer");
db.Computers
.Where(c => c.Id == computerId)
.Include(c => c.Studenti)
.FirstOrDefault()?
.Studenti
.ToList()
.ForEach(Console.WriteLine);
Console.WriteLine("Versione con Join");
db.Utilizzi
.Where(u => u.ComputerId == computerId && u.DataOraInizioUtilizzo >= DateTime.Now.AddDays(-30))
.Join(db.Studenti.Include(s => s.Classe),
u => u.StudenteId,
s => s.Id,
(u, s) => new { s, u.DataOraInizioUtilizzo })
.OrderBy(a => a.s.Classe.Nome)
.ThenByDescending(a => a.DataOraInizioUtilizzo)
.ToList()
.ForEach(a => Console.WriteLine($"Studente = {a.s}, Data utilizzo computer = {a.DataOraInizioUtilizzo}"));
}
//Q6: Stampa per ogni classe quanti utilizzi di computer sono stati fatti negli ultimi 30 giorni.
private static void Q6(UtilizziPCContext db)
{
Console.WriteLine("Versione con Navigation Property");
db.Utilizzi
.Where(u => u.DataOraInizioUtilizzo >= DateTime.Now.AddDays(-30))
.GroupBy(u => u.Studente.Classe.Nome)
.Select(g => new { NomeClasse = g.Key, NumeroUtilizzi = g.Count() })
.ToList()
.ForEach(Console.WriteLine);
Console.WriteLine("Versione con Join");
db.Studenti.Join(db.Utilizzi.Where(u => u.DataOraInizioUtilizzo >= DateTime.Now.AddDays(-30)),
s => s.Id,
u => u.StudenteId,
(s, u) => new { NomeClasse = s.Classe.Nome, u })
.GroupBy(a => a.NomeClasse)
.Select(g => new { NomeClasse = g.Key, NumeroUtilizzi = g.Count() })
.ToList()
.ForEach(Console.WriteLine);
}
//Q7: Stampa le classi che hanno utilizzato maggiormente i computer (quelle con il maggior numero di utilizzi) negli ultimi 30 giorni
private static void Q7(UtilizziPCContext db)
{
Console.WriteLine("Versione con Navigation Property");
var nUtilizziPerClasse = db.Utilizzi
.Where(u => u.DataOraInizioUtilizzo >= DateTime.Now.AddDays(-30))
.GroupBy(u => u.Studente.Classe)
.Select(g => new { NomeClasse = g.Key.Nome, NumeroUtilizzi = g.Count() });
var classiConNumeroUtilizziMax = nUtilizziPerClasse
.Where(u => u.NumeroUtilizzi == nUtilizziPerClasse.Max(u => u.NumeroUtilizzi));
classiConNumeroUtilizziMax
.ToList()
.ForEach(Console.WriteLine);
Console.WriteLine("Versione con Join");
//con una prima query sul Database calcoliamo il numero di utilizzi per classe
//Il risultato è un oggetto che implementa IQueryable sul database
var numeroUtilizziPerClasse = db.Studenti
.Join(db.Utilizzi.Where(u => u.DataOraInizioUtilizzo >= DateTime.Now.AddDays(-30)),
s => s.Id,
u => u.StudenteId,
(s, u) => new { NomeClasse = s.Classe.Nome, u })
.GroupBy(a => a.NomeClasse)
.Select(g => new { NomeClasse = g.Key, NumeroUtilizzi = g.Count() });
//Con una seconda query sul Database troviamo le classi che hanno il numero massimo di utilizzi
var classiConNumeroUtilizziMassimo = numeroUtilizziPerClasse
.Where(u => u.NumeroUtilizzi == numeroUtilizziPerClasse.Max(u => u.NumeroUtilizzi));
//stampiamo il risultato nell'applicazione
classiConNumeroUtilizziMassimo
.ToList()
.ForEach(Console.WriteLine);
}
static void CancellazioneDb(UtilizziPCContext db)
{
WriteLineWithColor("Attenzione, tutto il database andrà eliminato! Questa operazione non può essere revocata.\nSei sicuro di voler procedere? [Si, No]", ConsoleColor.Red);
bool dbErase = Console.ReadLine()?.ToLower().StartsWith("si") ?? false;
if (dbErase)
{
if (db.Database.EnsureDeleted())
{
Console.WriteLine("Database cancellato correttamente");
}
else
{
Console.WriteLine("Il database non esisteva e non è stata fatta alcuna azione");
}
}
}
//Stampa a console con il colore di foreground selezionato e successivamente ripristina il colore precedente
static void WriteLineWithColor(string text, ConsoleColor consoleColor)
{
ConsoleColor previousColor = Console.ForegroundColor;
Console.ForegroundColor = consoleColor;
Console.WriteLine(text);
Console.ForegroundColor = previousColor;
}
}

Ottieni il codice

Creare un’applicazione .Net Core per la gestione dei corsi di laurea universitari. Usando il framework EF Core creare il database dei corsi di laurea e degli studenti, sapendo che nel Model sono previste le seguenti classi:

Studente (Matricola, Nome, Cognome, CorsoLaureaId*, AnnoNascita )
CorsoLaurea(CorsoLaureaId, TipoLaurea, Facoltà )
Frequenta (Matricola*,CodCorso* )
Corso (CodiceCorso, Nome, CodDocente* )
Docente(CodDocente, Nome, Cognome, Dipartimento )
Dove:

  • Facoltà è un enumerativo che contiene i valori: Medicina, Ingegneria, MatematicaFisicaScienze, Economia
  • Dipartimento è un enumerativo che contiene i valori: Matematica, Fisica, Economia, IngegneriaCivile, IngegneriaInformatica, Medicina.
  • TipoLaurea è un enumerativo che contiene i valori: Triennale, Magistrale

Nello schema precedente si è usata la convenzione di indicare la chiave primaria con attributi sottolineati, mentre la chiave esterna è indicata con un asterisco.

La creazione del modello e del DBContext procede in maniera del tutto analoga a quanto già visto negli esempi precedenti.

Corso.cs
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
namespace Universita.Model;
public class Corso
{
//qui l'annotation è resa necessaria perché non si segue la convenzione sul nome della chiave
[Key]
public int CodiceCorso { get; set; }
public string Nome { get; set; } = null!;
//qui l'annotation è resa necessaria perché non si segue la convenzione sul nome della chiave
public int? CodDocente { get; set; }
//NAVIGATION PROPERTY
[ForeignKey("CodDocente")]
public Docente? Docente { get; set; }
//NAVIGATION PROPERTY PER MOLTI A MOLTI STUDENTI - CORSI
//https://learn.microsoft.com/en-us/ef/core/modeling/relationships/navigations
public ICollection<Frequenta> Frequenze { get; set; } = null!;
//skip navigation - https://learn.microsoft.com/en-us/ef/core/modeling/relationships/many-to-many
public ICollection<Studente> Studenti { get; set; } = null!;
}
CorsoLaurea.cs
namespace Universita.Model;
public class CorsoLaurea
{
//Chiave primaria
public int CorsoLaureaId { get; set; }
public TipoLaurea TipoLaurea { get; set; }
public Facoltà Facoltà { get; set; }
//NAVIGATION PROPERTY
public ICollection<Studente> Studenti { get; set; } = null!;
}
Docente.cs
using System.ComponentModel.DataAnnotations;
namespace Universita.Model;
public class Docente
{
//qui l'annotation è resa necessaria perché non si segue la convenzione sul nome della chiave
[Key]
public int CodDocente { get; set; }
public string Nome { get; set; } = null!;
public string Cognome { get; set; } = null!;
public Dipartimento Dipartimento { get; set; }
//NAVIGATION PROPERTY
public ICollection<Corso> Corsi { get; set; } = null!;
}
Frequenta.cs
namespace Universita.Model;
public class Frequenta
{
//la chiave primaria è composta da Matricola e CodCorso tramite Fluent API
//le chiavi esterne vanno configurate mediante Fluent API
public int Matricola { get; set; }
//NAVIGATION PROPERTY
public Studente Studente { get; set; } = null!;
public int CodCorso { get; set; }
//NAVIGATION PROPERTY
public Corso Corso { get; set; } = null!;
}
SharedEnums.cs
namespace Universita.Model;
public enum Facoltà
{
Medicina,
Ingegneria,
MatematicaFisicaScienze,
Economia
}
public enum Dipartimento
{
Matematica,
Fisica,
Economia,
IngegneriaCivile,
IngegneriaInformatica,
Medicina
}
public enum TipoLaurea
{
Triennale,
Magistrale
}
Studente.cs
using System.ComponentModel.DataAnnotations;
namespace Universita.Model;
public class Studente
{
//qui l'annotation è resa necessaria perché non si segue la convenzione sul nome della chiave
[Key]
public int Matricola { get; set; }
public string Nome { get; set; } = null!;
public string Cognome { get; set; } = null!;
public int AnnoNascita { get; set; }
//Chiave esterna
public int CorsoLaureaId { get; set; }
//NAVIGATION PROPERTY PER CHIAVE ESTERNA
public CorsoLaurea CorsoLaurea { get; set; } = null!;
//NAVIGATION PROPERTY PER MOLTI A MOLTI STUDENTI - CORSI
public ICollection<Frequenta> Frequenze { get; set; } = null!;
//skip navigation - https://learn.microsoft.com/en-us/ef/core/modeling/relationships/many-to-many
public ICollection<Corso> Corsi { get; set; } = null!;
}

Il DbContext è definito attraverso il file UniversitàContext.cs relativo alla definizione della classe UniversitàContext:

UniversitàContext.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Universita.Model;
namespace Universita.Data;
public class UniversitaContext : DbContext
{
//creazione delle tabelle
public DbSet<Studente> Studenti { get; set; } = null!;
public DbSet<CorsoLaurea> CorsiLaurea { get; set; } = null!;
public DbSet<Frequenta> Frequenze { get; set; } = null!;
public DbSet<Corso> Corsi { get; set; } = null!;
public DbSet<Docente> Docenti { get; set; } = null!;
public string DbPath { get; }
public UniversitaContext()
{
var dir = AppContext.BaseDirectory;
var path = Path.Combine(dir, "../../../universita.db");
DbPath = path;
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//per gestire la conversione da Enumerativo a string:
//https://learn.microsoft.com/en-us/ef/core/modeling/value-conversions
var converterTipoLaurea = new EnumToStringConverter<TipoLaurea>();
var converterFacolta = new EnumToStringConverter<Facoltà>();
var converterDipartimento = new EnumToStringConverter<Dipartimento>();
modelBuilder
.Entity<CorsoLaurea>()
.Property(cl => cl.TipoLaurea)
.HasConversion(converterTipoLaurea);
modelBuilder
.Entity<CorsoLaurea>()
.Property(cl => cl.Facoltà)
.HasConversion(converterFacolta);
modelBuilder
.Entity<Docente>()
.Property(d => d.Dipartimento)
.HasConversion(converterDipartimento);
//https://learn.microsoft.com/en-us/ef/core/modeling/relationships/many-to-many
modelBuilder.Entity<Corso>()
.HasMany(c => c.Studenti)
.WithMany(s => s.Corsi)
.UsingEntity<Frequenta>
(
//chiave esterna su Studente
left => left
.HasOne(fr => fr.Studente)
.WithMany(s => s.Frequenze)
.HasForeignKey(fr => fr.Matricola),
//chiave esterna su Corso
right => right
.HasOne(fr => fr.Corso)
.WithMany(c => c.Frequenze)
.HasForeignKey(fr => fr.CodCorso),
//primary key - in realtà, avendo associato le chiavi esterne, EF Core sarebbe in grado di creare
//la primary key a partire dalla combinazione delle due foreigh key. In questo caso particolare
//questa terza fluent API è ridondante, ma è riportata per mostrare come si potrebbe configurare manualmente
//la chiave primaria
k => k.HasKey(fr => new { fr.Matricola, fr.CodCorso }));
}
}

Dopo aver fatto la Migration del database, si scriva il codice delle query seguenti:
Q1: Stampare l’elenco degli studenti
Q2: Stampare l’elenco dei corsi
Q3: Modificare il docente di un corso di cui è noto l’id
Q4: Stampare il numero di corsi seguiti dallo studente con id = 1
Q5: Stampare il numero di corsi seguiti dallo studente con Nome=“Giovanni” e Cognome =“Casiraghi”
Q6: Stampare il numero di corsi seguiti da ogni studente
Q7: Stampare i corsi seguiti dallo studente con Nome=“Piero” e Cognome =“Gallo”

Program.cs
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Universita.Data;
using Universita.Model;
namespace Universita;
class Program
{
/// <summary>
/// Stampa a Console l'elenco degli studenti
/// </summary>
public static void PrintStudents()
{
using var db = new UniversitaContext();
//leggo gli studenti
List<Studente> studenti = [.. db.Studenti];
//stampo gli studenti
studenti.ForEach(s => Console.WriteLine($"Matricola = {s.Matricola}, Nome = {s.Nome}, Cognome = {s.Cognome}"));
}
/// <summary>
/// Stampa a console l'elenco dei corsi
/// </summary>
public static void PrintCourses()
{
//leggo gli studenti
using var db = new UniversitaContext();
List<Corso> corsi = [.. db.Corsi];
corsi.ForEach(s => Console.WriteLine($"CodCorso = {s.CodiceCorso}, Nome = {s.Nome}, CodDocente = {s.CodDocente}"));
}
/// <summary>
/// Un metodo che stampa i corsi seguiti da uno studente di cui si conosce nome e cognome
/// </summary>
/// <param name="nomeStudente">Nome dello studente</param>
/// <param name="cognomeStudente">Nome dello studente</param>
public static void PrintCorsiDiStudente(string nomeStudente, string cognomeStudente)
{
//trovare i corsi seguiti da uno studente - doppio join
using var db = new UniversitaContext();
//attenzione - potrebbero esserci casi di omonimia!
var corsiFrequentatiDaStudente = db.Studenti
.Where(s => s.Nome.ToUpper().Equals(nomeStudente.ToUpper())
&& s.Cognome.ToUpper().Equals(cognomeStudente.ToUpper()))
.Join(db.Frequenze,
s => s.Matricola,
f => f.Matricola,
(s, f) => new { f.CodCorso });
//Console.WriteLine($"\nQuery eseguita sul database per {nameof(corsiFrequentatiDaStudente)} = {corsiFrequentatiDaStudente.ToQueryString()}\n");
var dettaglioCorsiFrequentati = corsiFrequentatiDaStudente
.Join(db.Corsi,
cf => cf.CodCorso,
c => c.CodiceCorso,
(cf, c) => c);
//Console.WriteLine($"\nQuery eseguita sul database per {nameof(dettaglioCorsiFrequentati)} = {dettaglioCorsiFrequentati.ToQueryString()}\n");
dettaglioCorsiFrequentati.ToList().ForEach(c => Console.WriteLine($"Nome Corso = {c.Nome}"));
Console.WriteLine("Altro metodo - uso di navigation property");
//altro metodo - navigation property
db.Studenti
.Where(s => s.Nome.ToUpper().Equals(nomeStudente.ToUpper())
&& s.Cognome.ToUpper().Contains(cognomeStudente.ToUpper()))
//how to flatten a list of list in C#: https://stackoverflow.com/questions/1145558/linq-list-of-lists-to-single-list
.SelectMany(x => x.Corsi)
.ToList()
.ForEach(c => Console.WriteLine($"Nome Corso = {c.Nome}"));
}
/// <summary>
/// Stampa a console il numero di corsi seguiti da uno studente
/// </summary>
/// <param name="codStudente">Codice dello studente di cui si vuole contare i corsi</param>
public static void PrintNumeroCorsiDiStudente(int codStudente)
{
using var db = new UniversitaContext();
//uso della tabella di collegamento molti a molti - Frequenta in questo caso
Console.WriteLine("Uso della tabella di collegamento molti a molti - Frequenta");
var numeroCorsiFrequentatiDaStudente = db.Frequenze.Where(f => f.Matricola == codStudente).Count();
Console.WriteLine($"Numero corsi frequentati dallo studente con Matricola = {codStudente} " +
$"-> numero corsi: {numeroCorsiFrequentatiDaStudente}");
//Uso di collection property
Console.WriteLine("Uso di collection property");
db.Studenti.
Where(s => s.Matricola == codStudente)
.Select(s => new { s.Matricola, s.Nome, s.Cognome, NumeroCorsi = s.Corsi.Count })
.ToList()
.ForEach(s => Console.WriteLine($"Numero corsi frequentati dallo studente matricola {s.Matricola} {s.Nome} {s.Cognome} -> numero corsi: {s.NumeroCorsi}"));
//Uso di Entry per recuperare dati di una collection collegata a un oggetto già caricato in memoria
Console.WriteLine("Uso di Entry per recuperare dati di una collection collegata a un oggetto già caricato in memoria");
var studente = db.Studenti.Where(s => s.Matricola == codStudente).FirstOrDefault();
if (studente != null)
{ //come effettuare una query su una collection property: https://www.entityframeworktutorial.net/EntityFramework4.3/explicit-loading-with-dbcontext.aspx
var numeroCorsiFrequentatiDaStudente2 = db.Entry(studente).Collection(s => s.Corsi).Query().Count();
Console.WriteLine($"Numero corsi frequentati dallo studente con Matricola = " +
$"{codStudente} -> numero corsi: {numeroCorsiFrequentatiDaStudente2}");
}
}
/// <summary>
/// Stampa a console il numero di corsi seguiti da uno studente
/// </summary>
/// <param name="nomeStudente"></param>
/// <param name="cognomeStudente"></param>
public static void PrintNumeroCorsiDiStudente(string nomeStudente, string cognomeStudente)
{
using var db = new UniversitaContext();
//https://docs.microsoft.com/en-us/ef/core/miscellaneous/collations-and-case-sensitivity
//https://medium.com/@bridgesquared/efcore-sqlite-case-insensitive-order-21371b256e5
//https://github.com/dotnet/efcore/issues/8033
//per SQLite la collation predefinita è case sensitive
//versione con join e group by
db.Studenti
.Where(s => s.Nome.ToUpper().Equals(nomeStudente.ToUpper())
&& s.Cognome.ToUpper().Equals(cognomeStudente.ToUpper()))
.Join(db.Frequenze,
s => s.Matricola,
f => f.Matricola,
(s, f) => new { s.Matricola, s.Nome, s.Cognome, f.CodCorso })
.GroupBy(t => t.Matricola)
.Select(g => new { Matricola = g.Key, g.First().Nome, g.First().Cognome, NumeroCorsi = g.Count() })
.ToList()
.ForEach(s => Console.WriteLine($"Numero corsi frequentati dallo studente matricola " +
$"{s.Matricola} {s.Nome} {s.Cognome} -> numero corsi: {s.NumeroCorsi}"));
//Uso di collection property
//poiché vengono dati in input nome e cognome, ci potrebbero essere più studenti con lo stesso nome e cognome
//quindi il risultato dovrebbe riportare, per ciascuno il numero di conrsi seguiti
Console.WriteLine("Uso di collection property");
db.Studenti
.Where(s => s.Nome.ToUpper().Contains(nomeStudente.ToUpper())
&& s.Cognome.ToUpper().Contains(cognomeStudente.ToUpper()))
.Select(s => new { s.Matricola, s.Nome, s.Cognome, NumeroCorsi = s.Corsi.Count })
.ToList()
.ForEach(s => Console.WriteLine($"Numero corsi frequentati dallo studente matricola " +
$"{s.Matricola} {s.Nome} {s.Cognome} -> numero corsi: {s.NumeroCorsi}"));
}
/// <summary>
/// Modifica il docente di un corso di cui è noto l’id
/// </summary>
/// <param name="codCorso">codice del corso</param>
/// <param name="nuovoCodDocente">nuovo codice del docente</param>
public static void ModificaDocenteCorso(int codCorso, int nuovoCodDocente)
{
using var db = new UniversitaContext();
//accedo al corso
var corso = db.Corsi.Find(codCorso);
//accedo al docente per verificare che esiste quel docente
var docente = db.Docenti.Find(nuovoCodDocente);
//se esiste il docente con il nuovoCodDocente aggiorno il corso
if (docente != null && corso != null)
{
corso.CodDocente = nuovoCodDocente;
db.SaveChanges();
Console.WriteLine("Aggiornamento effettuato");
}
else if (docente == null)
{
Console.WriteLine("Impossibile aggiornare, il docente con il codice specificato non esiste");
}
else if (corso == null)
{
Console.WriteLine("Impossibile aggiornare, il corso il codice specificato non esiste");
}
}
/// <summary>
/// Per ogni studente stampa il numero di corsi frequentati
/// </summary>
public static void PrintNumeroCorsiFrequentatiPerStudente()
{
//raggruppo su Frequenta per Matricola e poi faccio la join con Studente per avere i dati di ciascuno studente
using var db = new UniversitaContext();
//MOLTO IMPORTANTE: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.x/breaking-changes
//https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.x/breaking-changes#linq-queries-are-no-longer-evaluated-on-the-client
//https://stackoverflow.com/questions/58138556/client-side-groupby-is-not-supported
//https://stackoverflow.com/a/60778664
//The LINQ GroupBy is much different from the SQL GROUP BY statement:
//LINQ just divides the underlying collection into chunks depending on a key,
//while SQL additionally applies an aggregation function to condense each of these chunks down into a single value.
var grouped = db.Frequenze.GroupBy(f => f.Matricola).Select(e => new { e.Key, Count = e.Count() });
//vedere anche
//https://www.thinktecture.com/en/entity-framework-core/hidden-group-by-capabilities-in-3-0-part-1/
//https://www.thinktecture.com/en/entity-framework-core/hidden-group-by-capabilities-in-3-0-part-2/
//https://stackoverflow.com/questions/37527783/get-sql-code-from-an-entity-framework-core-iqueryablet
//https://stackoverflow.com/a/51583047
//richiede using Microsoft.EntityFrameworkCore;
//Console.WriteLine($"\nQuery eseguita sul database per {nameof(grouped)} = {grouped.ToQueryString()}\n");
var result = grouped
.Join(db.Studenti,
group => group.Key,
s => s.Matricola,
(group, s) => new { s.Matricola, s.Nome, s.Cognome, NumeroCorsiFrequentati = group.Count });
//Console.WriteLine($"\nQuery eseguita sul database per {nameof(result)} = {result.ToQueryString()}\n");
//stampa risultato
result.ToList().ForEach(r => Console.WriteLine(r));
////IL SEGUENTE APPROCCIO NON FUNZIONA DA EC 3.X IN AVANTI
////il problema si ha quando si tenta di passare dalla GroupBy al ToList
//var grouped2 = db.Frequenta.GroupBy(f => f.Matricola);
//var result2 = grouped2.Join(db.Studenti,
// group => group.Key,
// s => s.Matricola,
// (group, s) => new { s.Matricola, s.Nome, s.Cognome, NumeroCorsiFrequentati = group.Count() });
////stampa risultato
////qui ho un errore perché si tenta di trasformare la group by dell'SQL (aggregazione) in un collection di collection
//result2.ToList().ForEach(r => Console.WriteLine(r));
}
static void InitTest()
{
UniversitaContext db = new();
//verifichiamo se il database esista già
//https://medium.com/@Usurer/ef-core-check-if-db-exists-feafe6e36f4e
//https://stackoverflow.com/questions/33911316/entity-framework-core-how-to-check-if-database-exists
if (db.Database.GetService<IRelationalDatabaseCreator>().Exists())
{
WriteLineWithColor("Il database esiste già, vuoi ricrearlo da capo? Tutti i valori precedentemente inseriti verranno persi. [Si, No]", ConsoleColor.Red);
bool dbErase = Console.ReadLine()?.StartsWith("si", StringComparison.CurrentCultureIgnoreCase) ?? false;
if (dbErase)
{
//cancelliamo il database se esiste
db.Database.EnsureDeleted();
//ricreiamo il database a partire dal model (senza dati --> tabelle vuote)
db.Database.EnsureCreated();
//inseriamo i dati nelle tabelle
PopulateDb();
Console.WriteLine("Database ricreato correttamente");
}
}
else //il database non esiste
{
//ricreiamo il database a partire dal model (senza dati --> tabelle vuote)
db.Database.EnsureCreated();
//popoliamo il database
PopulateDb();
Console.WriteLine("Database creato correttamente");
}
static void PopulateDb()
{
//1) inserisco istanze nelle tabelle che non hanno chiavi esterne -->CorsoDiLaurea, Docente
//creo una lista di CorsoDiLaurea e di Docente
List<Docente> docenti =
[
new (){CodDocente=1, Cognome="Malafronte", Nome="Gennaro",Dipartimento=Dipartimento.IngegneriaInformatica },
new (){CodDocente=2, Cognome="Rossi", Nome="Mario", Dipartimento=Dipartimento.Matematica},
new (){CodDocente=3, Cognome="Verdi", Nome="Giuseppe", Dipartimento=Dipartimento.Fisica},
new (){CodDocente=4, Cognome= "Smith", Nome="Albert", Dipartimento=Dipartimento.Economia}
];
List<CorsoLaurea> corsiDiLaurea =
[
new (){CorsoLaureaId = 1,TipoLaurea=TipoLaurea.Magistrale, Facoltà=Facoltà.Ingegneria},
new (){CorsoLaureaId = 2,TipoLaurea=TipoLaurea.Triennale, Facoltà=Facoltà.MatematicaFisicaScienze},
new (){CorsoLaureaId = 3,TipoLaurea=TipoLaurea.Magistrale, Facoltà=Facoltà.Economia},
];
using (var db = new UniversitaContext())
{
docenti.ForEach(d => db.Add(d));
corsiDiLaurea.ForEach(cl => db.Add(cl));
db.SaveChanges();
}
//2) inserisco altre istanze: Inserisco istanze di Corso e di Studente
List<Corso> corsi =
[
new (){CodiceCorso=1,Nome="Fondamenti di Informatica 1", CodDocente=1},
new (){CodiceCorso=2,Nome="Analisi Matematica 1", CodDocente=2},
new (){CodiceCorso=3,Nome="Fisica 1", CodDocente=3},
new (){CodiceCorso=4, Nome="Microeconomia 1", CodDocente=4}
];
List<Studente> studenti =
[
new (){Matricola=1, Nome="Giovanni", Cognome="Casiraghi", CorsoLaureaId=1, AnnoNascita=2000},
new (){Matricola=2, Nome="Alberto", Cognome="Angela", CorsoLaureaId=2, AnnoNascita=1999},
new (){Matricola=3, Nome="Piero", Cognome="Gallo", CorsoLaureaId=3, AnnoNascita=2000}
];
using (var db = new UniversitaContext())
{
corsi.ForEach(c => db.Add(c));
studenti.ForEach(s => db.Add(s));
db.SaveChanges();
}
//4) inserisco le frequenze - è la tabella molti a molti
List<Frequenta> frequenze =
[
new (){Matricola=1, CodCorso=1},// Giovanni Casiraghi frequenta il corso di Fondamenti di Informatica 1
new (){Matricola=1, CodCorso=2},// Giovanni Casiraghi frequenta il corso di Analisi Matematica 1
new (){Matricola=2, CodCorso=2},
new (){Matricola=2, CodCorso=3},
new (){Matricola=3, CodCorso=4}
];
using (var db = new UniversitaContext())
{
frequenze.ForEach(f => db.Add(f));
db.SaveChanges();
}
}
}
//stampa a console con il colore di foreground selezionato e successivamente ripristina il colore precedente
static void WriteLineWithColor(string text, ConsoleColor consoleColor)
{
ConsoleColor previousColor = Console.ForegroundColor;
Console.ForegroundColor = consoleColor;
Console.WriteLine(text);
Console.ForegroundColor = previousColor;
}
static void Main(string[] args)
{
InitTest();
WriteLineWithColor("\nEsecuzione di Q1:" +
"\nStampare l'elenco degli studenti: ", ConsoleColor.Cyan);
PrintStudents();
WriteLineWithColor("\nEsecuzione di Q2:" +
"\nStampare l'elenco dei corsi", ConsoleColor.Cyan);
PrintCourses();
WriteLineWithColor("\nEsecuzione di Q3:" +
"\nModificare il docente di un corso di cui è noto l'id:", ConsoleColor.Cyan);
ModificaDocenteCorso(1, 1);
WriteLineWithColor("\nDopo la modifica i corsi sono i seguenti:", ConsoleColor.Cyan);
PrintCourses();
WriteLineWithColor("\nEsecuzione di Q4:" +
"\nStampare il numero di corsi seguiti dallo studente con id = 1:", ConsoleColor.Cyan);
PrintNumeroCorsiDiStudente(1);
WriteLineWithColor("\nEsecuzione di Q5:" +
"\nStampare il numero di corsi seguiti dallo studente con Nome=\"Giovanni\" e Cognome =\"Casiraghi\"", ConsoleColor.Cyan);
PrintNumeroCorsiDiStudente("Giovanni", "Casiraghi");
WriteLineWithColor("\nEsecuzione di Q6:" +
"\nStampare il numero di corsi seguiti da ogni studente", ConsoleColor.Cyan);
PrintNumeroCorsiFrequentatiPerStudente();
WriteLineWithColor("\nEsecuzione di Q7:" +
"\nStampare i corsi seguiti dallo studente con Nome=\"Piero\" e Cognome =\"Gallo\"", ConsoleColor.Cyan);
PrintCorsiDiStudente("Piero", "Gallo");
WriteLineWithColor("Finito!", ConsoleColor.Cyan);
}
}