Introduzione a Entity Framework 4.0
Scritto da
Alessandro Mostarda
il
mercoledì 25 agosto 2010
Linguaggio:
•
Framework:
•
Livello: 100
Download sorgenti
La nuova release di Visual studio 2010
ha portato con se una nuova versione di Entity Framework (4.0).
Rispetto al predecessore sono stati introdotte nuove funzionalità
ed apportati notevoli miglioramenti, il che rende questo ORM uno
strumento di sviluppo veramente valido per la gestione della
persistenza dei dati sul DB.
In questo articolo non si parlerà del confronto con la versione
precedente, ma verrà illustrato il funzionamento partendo
dall'inizio.
Prima di addentrarci nello specifico, vorrei introdurre il
concetto di ORM. Un'ORM è un sistema software che consente di
interagire con un database mediante l'utilizzo di classi e di LINQ
e non di comandi standard SQL. Questo ci consente di slegarci dalla
struttura fisica e logica del database, e ci consente di avere un
approccio Object-Oriented.
Dopo questa brevissima introduzione andiamo a vedere come
iniziare ad utilizzare EF4. Per prima cosa dobbiamo aggiungere un
nuovo elemento al nostro progetto, come si evince dalla figura
sottostante:

Dopo aver selezionato la voce evidenziata, inizia il wizard di
creazione . E' necessario soffermarsi sulla prima richiesta del
wizard. Infatti ci viene chiesto se generare le classi da un
database esistente, oppure se generare prima le classi e, poi,
farci generare lo script di creazione del DB. Entrambe le modalità
presentano vantaggi e svantaggi. Infatti nel primo
caso(Database-First), possiamo decidere di creare un nuovo
modellandolo secondo le caratteristiche del db stesso oppure di
sviluppare l'applicazione nel caso che il database sia già stato
disegnato. L'altra modalità invece ci consente di concentrarci
sulla creazione delle nostre classi di dominio, rimandando la
creazione del database ad un secondo momento. Quando si termina di
generare tutte le classi di dominio, possiamo generare lo script
per il database tramite il designer incluso in Visual Studio. E'
chiaro che tale script è ignaro delle specifiche features che ci
mette a disposizione il motore del database su cui verranno
generate le tabelle. Tanto per fare un esempio lo script non
prevede la generazione di indici sulle tabelle.
E' comunque importante sapere che ci sono entrambe le modalità a
nostra disposizione ed abbiamo, quindi, la possibilità di
effettuare la scelta caso per caso.

Dopo aver eseguito tutti i passi necessari del wizard, Visual
Studio mostra l'editor di Entity Framework. All'interno di questo
editor troviamo l'insieme delle nostre tabelle che abbiamo
importato in precedenza.
Cosa che inizialmente può sembrare strana è che non vediamo le
stored procedure eventualmente importate, ma questo accade solo
perché, per utilizzarle, dobbiamo fare un'operazione
sull'editor.

Infatti dobbiamo cliccare con il testo destro ed effettuare
l'operazione evidenziata nell'immagine sopra. A questo punto
l'editor ci mostra questa finestra che ci serve per impostare tutti
i parametri necessari per l'utilizzo di stored procedure.

Selezioniamo la nostra stored procedure, clicchiamo sul tasto
"Get Column Information", e poi clicchiamo sul "Create New Complex
Type" per farci generare una nuova classe che conterrà il risultato
della nostra Stored Procedure, Se invece la nostra stored procedure
contiene esattamente gli stessi campi di una classe generata in
precedenza, allora possiamo decidere di mapparla verso una Entity
già esistente.
Al termine di queste operazioni di configurazione sul nostro
editor, Entity Framework genera per noi tutte le classi necessarie
per il colloquio con il db. Infatti, da adesso, abbiamo a
disposizione nel nostro progetto una Classe Context (EF4Entitites
nel progetto di esempio), che si occupa di aprire la connessione
con il database e di effettuare le query e/o gli aggiornamenti.
Questa classe contiene tante proprietà quante sono le nostre entity
ed inoltre contiene tanti metodi quante sono le nostre stored
procedure. Inoltre, vengono, anche, generate le classi che
mappano le tabelle e/o stored procedure, come si può notare dal
codice sorgente allegato.
Il linguaggio designato per interrogare il nostro DB è Linq to
Entites, pertanto effettueremo le nostre query, quasi allo stesso
modo con cui effettuamo query sulle normali collection utilizzando
LINQ. In realtà non tutti i comandi di LINQ sono a disposizione, in
quanto alcuni di essi non sono trasformabili in codice SQL,
pertanto diciamo che LINQ to Entities contiene un set leggermente
ridotto di istruzioni rispetto a LINQ to Objects.
SELECT
Ma vediamo ora alcuni esempi pratici per capirne il
funzionamento. Se vogliamo recuperare l'elenco completo dei nostri
clienti la sintassi è la seguente:
using
(ef4Entities ctx = new
ef4Entities())
{
Griglia.DataSource = ctx.Clienti;
}
Già a primo impatto possiamo notare l'esigua quantità di codice
necessaria per avere a disposizione il risultato. Questo è uno dei
pregi fondamentali di Entity Framework, poiché la produttività
aumenta in maniera decisa dovendo scrivere pochissime righe di
codice.
WHERE
Se invece volessimo avere l'elenco di tutti i clienti della
città di Roma, scriveremmo la nostra query in questo modo:
using
(ef4Entities ctx = new
ef4Entities())
{
Griglia.DataSource = ctx.Clienti.Where(w =>
w.Citta == "Roma");
}
ORDER BY
La prossima query ci restituisce l'elenco dei clienti ordinate
per nome:
using
(ef4Entities ctx = new
ef4Entities())
{
Griglia.DataSource = ctx.Clienti.OrderBy(o =>
o.Nome);
}
ANONYMOUS TYPE
La query successiva, invece, ci restituisce il nome, il
cognome ed il numero totale delle fatture emesse a ciascun
cliente:
using (ef4Entities ctx =
new ef4Entities())
{
Griglia.DataSource =(from x in ctx.Clienti
select
new
{Cognome = x.Cognome,
Nome = x.Nome,
NumeroFatture = x.Fatture.Count
}).OrderByDescending (o => o.NumeroFatture);
}
INSERT
Nell'esempio successivo verrà mostrato come inserire un nuovo
record. Notare che, per salvare i dati nel db, viene invocato
il metodo AddObject della proprietà Clienti dell'ObjectContext e
successivamente il metodo SaveChanges.
using (var ctx = new ef4Entities())
{
Clienti cliente= new Clienti();
cliente.Cognome = txtCognome.Text;
cliente.Nome = txtNome.Text;
cliente.Indirizzo = txtIndirizzo.Text;
cliente.Cap = txtCap.Text;
cliente.Citta = txtCitta.Text;
cliente.Provincia = txtProvincia.Text;
cliente.PartitaIva = txtPartitaIva.Text;
cliente.CodiceFiscale = txtCodiceFiscale.Text;
ctx.Clienti.AddObject(cliente);
ctx.SaveChanges();
}
UPDATE
Per quanto riguarda la modifica invece EF4 necessita di 2
passaggi. Il primo serve per recuperare l'oggetto cliente esistente
sul db, poi vengono modificati i valori ed infine chiamato il
metodo SaveChanges.
using (var
ctx = new ef4Entities())
{
Clienti cliente = ctx.Clienti.Where(w => w.Id
== Id).First();
cliente.Cognome = txtCognome.Text;
cliente.Nome = txtNome.Text;
cliente.Indirizzo = txtIndirizzo.Text;
cliente.Cap = txtCap.Text;
cliente.Citta = txtCitta.Text;
cliente.Provincia = txtProvincia.Text;
cliente.PartitaIva = txtPartitaIva.Text;
cliente.CodiceFiscale =
txtCodiceFiscale.Text;
ctx.SaveChanges();
}
DELETE
La cancellazione invece avviene con questo codice:
using (ef4Entities ctx = new ef4Entities())
{
int IdCliente = (int)Griglia.SelectedRows[0].Cells[0].Value;
var cliente = ctx.Clienti.First(f => f.Id == IdCliente);
ctx.Clienti.DeleteObject(cliente);
ctx.SaveChanges();
}
STORED PROCEDURE
Il totale del fatturato dei nostri clienti può essere fatto
tramite una stored procedure che può essere invocata in questo
modo:
using (ef4Entities ctx = new ef4Entities())
{
Griglia.DataSource = ctx.FatturatoClienti();
}
LEFT JOIN + SUM
Se invece volessimo fare la stessa operazione tramite Linq to
entities il codice diventa più complesso:
using (var ctx = new ef4Entities())
{
Griglia.DataSource = (from clienti in ctx.Clienti
join fatture in ctx.Fatture on clienti.Id equals fatture.IdCliente into gruppo1
from lista1 in gruppo1.DefaultIfEmpty()
join righefattura in ctx.RigheFattura on lista1.Id equals righefattura.IdFattura into gruppo2
from lista2 in gruppo2.DefaultIfEmpty()
group lista2 by new { clienti.Cognome, clienti.Nome } into Finale
select new
{
Cognome = Finale.Key.Cognome,
Nome = Finale.Key.Nome,
Fatturato = Finale.Sum(s => s.PrezzoTotale + s.Imposta) == null ? 0 : Finale.Sum(s => s.PrezzoTotale + s.Imposta)
}).OrderByDescending(o => o.Fatturato);
}
In questo caso abbiamo implementato 2 left join tra le 3 tabelle
ed abbiamo estratto il risultato. La cosa più importante da notare
è l'istruzione "DefaultIfEmpty" che consente di trasformare un
operazione di INNER JOIN in un'operazione di LEFT JOIN.
MULTIPLE INSERT
L'ultima cosa da far vedere è l'aggiunta di una fattura, che
deve registrare sia i dati di testata, che quelli di dettaglio.
L'operazione può essere effettuate in questo modo.
using (var ctx = new ef4Entities())
{
Fatture fatt = new Fatture();
//Muovo i valori
fatt.IdCliente = (int)cmbClienti.SelectedValue;
fatt.DataEmissione = Convert.ToDateTime((txtData.Text));
fatt.Numero = txtNumero.Text;
//Creo le righe di dettaglio;
foreach (DataGridViewRow riga in Griglia.Rows)
{
var rigaFattura = new RigheFattura();
rigaFattura.DescrizioneArticolo = (string)riga.Cells[1].Value;
rigaFattura.PrezzoUnitario = (decimal)riga.Cells[2].Value;
rigaFattura.PrezzoTotale = (decimal)riga.Cells[3].Value;
rigaFattura.Imposta = (decimal)riga.Cells[4].Value;
fatt.RigheFattura.Add(rigaFattura);
}
//Creo l'oggetto fattura
ctx.Fatture.AddObject(fatt);
ctx.SaveChanges();
this.Hide();
}
La cosa che può risultare interessante è che, prima prepariamo
la nostra classe con la compilazione delle nostre righe di
dettaglio, e poi la aggiungiamo alla proprietà Fatture del nostro
ObjectContext. Infatti non è necessario invocare il metodo
AddObject per ciascuna riga di dettaglio della Fattura. Questo
avviene perché Entity framework aggiunge automaticamente tutti gli
elementi della collection righeFattura della classe Fattura.
L'ultima cosa di cui volevo parlare in questo articolo
introduttivo era del file EDMX. All'inizi di questo articolo si è
parlato del designer. In realtà tutte le informazioni relative al
designer vengono memorizzate in un file (EDMX), che altro non è che
un semplice file XML composto di 4 parti:
- SSDL: Questo sezione del file EDMX contiene la strutture delle
tabelle e delle stored procedure del Database
- CSDL: questa sezione contiene la struttura delle nostre classi,
che come vedremo nei prossimi può tranquillamente non coincidere
con le tabelle
- C-S: questa sezione contiene tutte le informazioni di mappatura
tra le tabelle del database e le nostre classi
- Designer: questa sezione contiene informazioni riguardanti solo
ed esclusivamente l'editor di Entity framework, e non ha alcun
impatto sul funzionamento di EF all'interno del nostro
applicativo.
Questa piccola appendice risulta utile in quanto in alcuni casi
potrà essere comodo e/o necessario modificare manualmente questo
file, piuttosto che utilizzare l'editor.
Conclusioni
Questo articolo risulta essere solo una breve introduzione a
Entity framework, per il quale servirebbe un libro per spiegarne
tutte le funzionalità. Nei prossimi articoli parleremo, invece,
delle features più avanzate. Ma già da adesso si può apprezzare
l'intuitività di questo tool, che ha lo scopo di facilitare
l'accesso ai dati e la mappatura dei dati del DB verso le nostre
classi.
Appendice
Per ragioni di spazio il codice è riportato solo in C#, ma,
nell'esempio allegato, è presente anche il sorgente nel linguaggio
VB.NET. Per far funzionare l'esempio allegato è necessario creare
un database su Sql server e ripristinare il backup (EF4.bak)
contenuto nello .zip allegato.
Tags: orm,Entity Framework