Pagina personale di:
Carlo Vecchio
appunti di C#, R, SQL Server, ASP.NET, algoritmi, numeri
Vai ai contenuti

C# - Entity Framework

C#
Entity Framework
  • Per utilizzare l’Entity Framework in un progetto, occorre fare un paio di passi: installarlo e creare l’Entity Data Model (EDM).
  • L’EDM è molto utilizzato nella pratica in quanto:
    • Permette di gestire ogni tabella come una classe.
    • Permette di utilizzare dei comodi metodi sulle tabelle, eventualmente utilizzando delle lamdba-expressions (cioè LINQ).

Installare l'Entity Framework nel progetto
  • Ambiente di sviluppo utilizzato: Visual Studio 2013 Professional, Update 5.
  • Si parte dal progetto aperto in Visual Studio.
  • In “Solution Explorer”, click destro sul progetto (no sulla soluzione), selezionare “Menage NuGet Packages”.
  • Nel pannello a sinistra, selezionare “Online”.
  • Selezionare il Package “Entity Framework”, attualmente alla versione 6.1.3. Per trovarlo più velocemente, nella casella di ricerca a destra, digitare “entity” e avviare la ricerca.
  • Una volta selezionato, appare il bottone “Install”.
  • Cliccare sul bottone “Install”.
  • Accettare la licenza.
  • Chiudere “Menage NuGet Packages”.
  • Se si controllano i riferimenti del progetto, si dovrebbero trovare: “EntityFramework” e “EntityFramework.SqlServer”.

Creare l'Entity Data Model (EDM)
  • Si parte ancora dal progetto aperto.
  • Controllare nelle proprietà del progetto, scheda “Application”, che il “Targer framework” sia la versione 4.5.1.
  • Click destro sul progetto (no sulla soluzione), “Add -> New Item…” e scegliere “ADO.NET Entity Data Model”. Cambiare il nome con uno a piacere, per esempio “Db”. Si apre il “Entity Data Model Wizard”.
  • Tra i modelli da scegliere, il mio preferito è: “Code First from database”. Tra i vari modelli è il più pulito e permette di fare modifiche al database e al modello stesso in modo veloce.
  • Scegliere quindi il modello e premere “Next”.
  • Scegliere il database: selezionarlo dalla combo box oppure creare una nuova connessione; poi premere “Next”.
  • Selezionare gli oggetti da includere nel modello; poi premere “Finish”.
  • Vengono aggiunte diverse classi, una per ogni tabella del database. Per organizzare meglio il codice, conviene spostare tutte queste classi in una cartella (per esempio “EntityDataModel”).
  • Vengono aggiunte anche le due classi “Db.cs” e “sysdiagram.cs”; anche queste conviene spostarle nella stessa cartella del punto precedente. La prima classe rappresenta l’EDM scelto dall’utente, la seconda è fatta dal Visual Studio.

Aggiornare l'Entity Data Model
  • Se si fanno delle modifiche al database occorre aggiornare l’Entity Data Model del progetto.
  • Avendo utilizzato il modello “Code First from database” non esiste il file *.edmx che dà la possibilità di eseguire un aggiornamento molto rapido. Per questo motivo bisogna fare le modifiche manualmente.
  • Le seguenti procedure sembrano funzionare, almeno con le versioni installate attualmente.

Aggiunta di una tabella
  • Creare un nuovo EDM e chiamarlo per esempio “DbTemp”. Per fare questo utilizzare la stessa procedura del paragrafo “Creare l’Entity Data Model (EDM)”. Avere però l’accortezza di selezionare la sola tabella che si vuole aggiungere.
    • Copiare il codice presente in “DbTemp.cs” nel file “Db.cs” (o, più dettagliatamente, bisogna fare il merge del nuovo EDM sul vecchio EDM). Questo è il passaggio più difficile; ecco i passi da seguire:
    • Copiare il codice della definizione del DbSet:

   public virtual DbSet<table_name> table_name { get; set; }

    • Copiare l’eventuale codice nell’evento “OnModelCreating()” da un file all’altro.
  • Spostare la classe con nome uguale alla tabella, nella cartella con le altre classi (in pratica riordinare tutte le classi nell’unica cartella, come consigliato).
  • Eliminare il file “DbTemp” che non serve più.
  • Nel file “App.config” eliminare la stringa di connessione “DbTemp”.

Eliminazione di una tabella
  • Nella classe che descrive l’EDM (nel mio caso “Db.cs”):
    • Eliminare la riga che definisce il DbSet:

   public virtual DbSet<table_name> table_name { get; set; }

    • Eliminare l’eventuale codice nell’evento “OnModelCreating()” che fa riferimento alla tabella da eliminare. Infatti in questo evento possono essere presenti delle righe del tipo:

   modelBuilder.Entity<table_name>()
       .Property(e => e.table_property)
       .IsFixedLength();

  • Eliminare le classi (presenti nella cartella “EntityDataModel” se si sono organizzati i file come consigliato) i cui nomi sono uguali alla tabella da eliminare.

Modifica di una tabella
  • Un metodo consiste in:
    • Eliminare la tabella dall’EDM, poi aggiungerla nuovamente.
    • In pratica utilizzare le procedure descritte sopra in questo stesso capitolo.

Utilizzare l'Entity Data Model
  • Di seguito, alcuni esempi su come utilizzare le entità messe a disposizione dell’EDM.
  • In tutti gli esempi successivi, il codice deve essere compreso all’interno dello ‘using’ seguente, che definisce una variabile tipicamente chiamata ‘contesto’. Naturalmente si dovrà cambiare il nome della classe ‘Db’ con quello utilizzato nel progetto (cioè con il nome dell’EDM’ definito precedentemente).

   using (var ctx = new Db())
   {
       ...
   }

  • Negli esempi successivi, si suppone di avere la tabella ‘People’ con i seguenti campi: ‘ID’, ‘Nome’ e ‘Cognome’.

Select
  • Esempi di estrazione dati in oggetti singoli, liste e dizionari.

   using (var ctx = new Db())
   {
       // First: primo record o una eccezione se non trovato.
       People p1 = ctx.People.First();

       // FirstOrDefault: primo record o null se non trovato.
       People p2 = ctx.People.Where(p => p.Nome == "Piero").FirstOrDefault();

       // Single: l'unico record o una eccezione se non trovato / trovati più di uno.
       People p4 = ctx.People.Where(p => p.Nome == "Piero").Single();

      // Clausola 'IN': se un campo può assumere più valori, non esiste la clausola 'IN' come in SQL.
      // La soluzione consiste nel definire un array ed utilizzare il metodo 'Contains'.
      // Per esempio:

      p => p.Nome == "aaa" || p.Nome == "bbb"
      Con MyArray = {"aaa", "bbb"}
      p => MyArray.Contains(p.Nome)

       // SingleOrDefault: l'unico record o null se non trovato, eccezione se trovati più di uno.
       People p5 = ctx.People.Where(p => p.Nome == "Piero").SingleOrDefault();

       // ToList(): lista.
       List<People> lst1 = ctx.People.ToList();

       // OrderBy: ordinamento ascendente.
       List<People> lst2 = ctx.People.OrderBy(p => p.Nome).ToList();

       // OrderBy: ordinamento discendente.
       List<People> lst3 = ctx.People.OrderByDescending(p => p.Nome).ToList();

       // OrderBy: ordinamento multiplo (ordinato per Nome - Cognome).
       List<People> lst4 = ctx.People.OrderBy(p => p.Nome).ThenBy(p => p.Cognome).ToList();

      // GroupBy: raggruppa in lista di oggetti anonimi (anonymous objects), per questo si deve utilizzare la parola chiave 'var'.
      var lst5 = ctx.People.GroupBy(p => p.Nome).Select(p => new { Nome = p.Key, Count = p.Count() }).ToList();
 
      // Si noti che la lst5 è una lista di oggetti anonimi, creati da:
      new { Nome = p.Key, Count = p.Count() }
      // Con p.Key si identifica la chiave del GroupBy che in questo esempio è stata nominata 'Nome' ed è una stringa.
 
      // GroupBy su due campi.
      var lst6 = ctx.People.GroupBy(p => new { p.Nome, p.Cognome }).Select(p => new { Nome = p.Key.Nome, Cognome = p.Key.Cognome, Count = p.Count() }).ToList();

       // GroupBy: raggruppa in dizionario.
       Dictionary<string, int> dic1 = ctx.People.GroupBy(p => p.Nome).Select(p => new { name = p.Key, count = p.Count() }).ToDictionary(p => p.name, p => p.count);
   }

Insert
  • Esempio di Insert nel database.

   People p1 = new People() { ID = 10, Nome = "Isaac", Cognome = "Newton" };
 
   using (var ctx = new TestEntities())
   {
       ctx.People.Add(p1);
       ctx.SaveChanges();
   }

Update
  • Esempio di Update nel database.

   People p1;
 
   // Legge l'oggetto da modificare.
   using (var ctx1 = new TestEntities())
   {
       p1 = ctx1.People.Where(p => p.ID == 10).FirstOrDefault<People>();
   }

   // Cambia qualche proprietà dell'oggetto.
   if (p1 != null)
   {
       p1.Nome = "David";
       p1.Cognome = "Hilbert";
   }

   // In un nuovo contesto...
   using (var ctx2 = new TestEntities())
   {
       // Marca l'entità come modificata.
       ctx2.Entry(p1).State = System.Data.Entity.EntityState.Modified;

       // Salva.
       ctx2.SaveChanges();
   }

Delete
  • Esempio di Delete nel database.

   People p1;

   // Legge l'oggetto da modificare.
   using (var ctx1 = new TestEntities())
   {
       p1 = ctx1.People.Where(p => p.ID == 10).FirstOrDefault<People>();
   }

   // In un nuovo contesto...
   using (var ctx2 = new TestEntities())
   {
       // Marca l'entità come modificata.
       ctx2.Entry(p1).State = System.Data.Entity.EntityState.Deleted;

       // Salva.
       ctx2.SaveChanges();
   }

Stored Procedures - Valore
  • Esempio di esecuzione di una Stored Procedure che restituisce un valore.
  • Si supponga di avere la tabella ‘tblParameters’ che contiene vari parametri utili al funzionamento di un programma.
  • Ecco lo script di creazione della tabella:

   CREATE TABLE tblParameters
   (
       Parameter     NVARCHAR(10),
       Description   NVARCHAR(50),
       Int01         INT,
       Int02         INT,
       Int03         INT
   )

  • Si abbiano i seguenti record:

   INSERT INTO tblParameters
   VALUES
   ('COUNTER', 'Contatore', 0, 0, 0),
   ('Param. X', 'Parametro X', 0, 0, 0),
   ('Param. Y', 'Parametro Y', 0, 0, 0)

  • Si vuole creare una Stored Procedure che riceva in input una stringa e che restituisca in ouput un valore int. La Stored Procedure deve incrementare il campo ‘Int01’ in corrispondenza al record identificato dal parametro passato in input, poi restituisce il valore incrementato. In altre parole la Stored Procedure gestisce un contatore univoco a livello di applicazione.
  • Ecco la Stored Procedure:

   CREATE PROCEDURE spGetCounter
     @Par NVARCHAR(10),
     @Counter INT OUTPUT
   AS
   BEGIN
     BEGIN TRAN
       UPDATE dbo.tblParameters
       SET @Counter = Int01 = Int01 + 1
       WHERE Parameter = @Par
     COMMIT TRAN
     SELECT @Counter
   END

  • Ecco il codice per verificare che funzioni la Stored Procedure:

   DECLARE @Val INT
   EXECUTE spGetCounter @Par = 'COUNTER', @Counter = @Val

  • Nell’EDM non si fa nessuna importazione della Stored Procedure, avendo utilizzato la tecnica del ‘Code First’, come descritto nel paragrafo ‘Creare l’Entity Data Model (EDM)’.
  • Infine ecco il codice C# per eseguire la Stored Procedure con l’Entity Framework.

   using (var ctx = new Db())
   {
       var param1 = new SqlParameter
       {
           ParameterName = "@Par",
           Value = "COUNTER",
           DbType = DbType.String,
           Size = 10,
           Direction = System.Data.ParameterDirection.Input
       };
 
       var param2 = new SqlParameter
       {
           ParameterName = "@Counter",
           DbType = DbType.Int32,
           Direction = System.Data.ParameterDirection.Output
       };

       ctx.Database.ExecuteSqlCommand("spGetCounter @Par, @Counter OUTPUT", param1, param2);

       MessageBox.Show(param2.Value.ToString());
   }

Stored Procedures - Set di dati
  • Esempio di esecuzione di una Stored Procedure che restituisce alcuni record di una tabella.
  • Si supponga di avere la tabella 'People' costituita dai campi 'Id', 'Nome', 'Cognome' e altri attributi, 'Filtro1', 'Filtro2' e 'Filtro3'.
  • La Stored Procedure restituisce alcuni record della tabella in base ai parametri passati. E restituice solo i primi tre campi.
  • La Stored Procedure è per esempio:

   CREATE PROCEDURE [dbo].[spGetPeople]
   @Filtro1 INT,
   @Filtro2 INT,
   @Filtro3 INT
   AS
       SELECT Id, Nome, Cognome
       FROM dbo.People
       WHERE 1 = 1
         AND Filtro1 = @Filtro1
         AND Filtro2 = @Filtro2
         AND Filtro3 = @Filtro3
   RETURN 0

  • È necessaria una classe di appoggio che mappi i campi restituiti dalla Stored Procedure.

   public class People
   {
       public int Id { get; set; }
       public string Nome { get; set; }
       public string Cognome { get; set; }
  }

  • Infine, ecco il codice per chiamare la Stored Procedure.

   var param1 = new SqlParameter { ParameterName = "@Filter1", Value = 10, DbType = DbType.Int32, Direction = System.Data.ParameterDirection.Input };
   var param2 = new SqlParameter { ParameterName = "@Filter2", Value = 20, DbType = DbType.Int32, Direction = System.Data.ParameterDirection.Input };
   var param3 = new SqlParameter { ParameterName = "@Filter3", Value = 30, DbType = DbType.Int32, Direction = System.Data.ParameterDirection.Input };

   List<People> peoples = ctx.Database.SqlQuery<People>("spGetPeople @Filter1, @Filter2, @Filter3 ", param1, param2, param3).ToList();

© 2022 Carlo Vecchio
Torna ai contenuti