LINQ to SQL - Parte I
LINQ rappresenta una delle novità più importanti per lo sviluppo nella prossima release di Visual Studio.
LINQ è una serie di estensioni al linguaggio, sia per C# che VB.NET, che ci permettono di utilizzare degli operatori "alla SQL" per lavorare su diverse sorgenti dati.
Questa è la terza parte di una serie di post. Negli altri post ho affrontato i segueti argomenti:
Ora vi racconto l'implementazione di Linq più interessante: LINQ to SQL. LINQ to SQL ed il designer di Visual Studio 2008 sono pensati per reallizzare applicazioni che usano SQL Server come database. Il designer genera molto del codice che fare a mano sarebbe tedioso, ma al contempo ci lascia la possibilità di intervenire con soluzioni più mirate, dandoci la possibilità di cambiare o estendere il codice generato.
LINQ to SQL
Linq to SQL vi permette di utilizzare la sintassi vista in precedenza per interrogare SQL Server, la query espressa verrà poi convertita in una chiamata ADO.NET. Visual Studio 2008 mette a disposizione nell' IDE un designer che ci aiuta, a partire da un database SQL Server esistente, a mappare le tabelle alle classi che useremo nella nostra applicazione.
Configuriamo Northwind
Negli esempi seguenti userò come database il famosissimo Northwind, che potete scaricare qui. Come DBMS ho usato SQL Server 2005 Express Edition SP2, che potete scaricare gratuitamente; allo stesso link potete trovare anche la SQL Server Management Studio Express, l'applicazione client per l'amminstrazione.
Aggiungiamo il database Northwind alla lista di quelli gestiti da SQL Server Express. Apriamo la console di SQL Server Management Studio Express.
Tasto destro su Databases, quindi Attach, appare la schermata sopra in figura, dove possiamo aggiungere il file Northwnd.MDF: premere il tasto Add ... e quindi scegliere la directory in cui è copiato il file.
Alla fine il nostro database verrà aggiunto alla lista di quelli gestiti. Chiudiamo il Management Studio.
Creiamo l'applicazione di test con Visual Studio 2008 ed usiamo il designer
Creiamo ora una console application con Visual Studio 2008, in VB.NET o in C#. Aggiungiamo alla lista delle connessioni in Visual Studio quella al database Northwind che verrà visualizzata nel Server Explorer (per visualizzarlo View -> Server Explorer). Quindi premendo il bottone "connect to database" possiamo visualizzare direttamente da Visual Studio il nostro database e avere accesso agli elementi che lo costituiscono: tabelle, store procedues, viste etc.
Per accedere al Designer che ci consente di effettuare il mappaggio delle tabelle alle classi aggiungiamo un nuovo item, tra la lista dei templates troviamo LINQ to SQL Classes come mostrato in figura.
Una volta rinominato il file in Northwnd.dbml e aggiunto al progetto, l' IDE di VS 2008 ci dà la possibilità di fare il drag-and-drop delle tabelle del database al fine di costruire le classi ad esse associate. Così in figura ho fatto il drag-and-drop delle tabelle Products, Categories, Orders, Customers che hanno generato le relative classi Product, Category, Order e Customer.
Se diamo un'occhiata ai file del nostro progetto vedrete che sono stati aggiunti tre file, se aprite il file Northwind.designer.cs potete notare che cosa ha creato il designer di Visual Studio.
Vi faccio notare che è stata generata una classe NorthwindDataContext che deriva dalla classe DataContex . Come vedrete a breve la query che faremo sul database sarà sullo stile di quelle viste con Linq To XML e Linq to Objects. La classe DataContext ha la funzione di "mascherare" la connessione verso il database e di rendere possibili le operazioni di aggiornamento verso il database. Ogni tabella del nostro database sarà accedibile come proprietà pubblica di questa classe e sarà, a sua volta, una classe di tipo Table. Ad esempio ci sarà la possibilità di accedere alla classe Products che è di tipo Table<Product> . La classe Product viene descritta nel prossimo paragrafo. In breve quindi la classe DataContext è l'entry point per poter utilizzare Linq To SQL.
Come funziona il mapping del designer tra il database e le classi ?
Sempre all'interno del file generato trovate una serie di attributi che specificano il mappaggio con il database, le tabelle e le classi. La classe NorthwindDataContext viene mappato sul database Northwind.
La classe Product viene mappata sulla tabella Products.
All'interno della classe Product la proprietà ProductID viene mappata sulla corrispondente colonna della tabella del db e vengono inoltre indicate altre caratteristiche: ad esempio che è un chiave primaria ed il tipo che la rappresenta nello schema del db. L'attributo AutoSync stabilisce che relazione di sincronizzazione ci deve essere tra la proprietà dell'oggetto e la row del db. Nel nostro caso la proprietà dell'oggetto viene valorizzato quando un prodotto viene effettivamente inserito nel database, notate che sul database la colonna è una IDENTITY. Qui il dettaglio sull'attributo Column.
Il mappaggio che abbiamo visto si basa sul fatto che le classi della nostra applicazione ad esempio Products, che useremo con LINQ, sono mappate tramite una serie di attributi al nostro database. Questa non è l'unica soluzione adottabile: è infatti possibile disaccopiare la classe dall'attributo che la mappa sul database e usare un file XML per effettuare tale mappaggio, ma questo sarà un argomento di un altro post ...Per chi vuole subito informazioni su come costruire il proprio modello ad oggetti con Linq To SQL, segua il link.
Una semplice query ...
Vogliamo ora ottenere il nome dei prodotti della categoria "Beverages", scriviamo quindi:
C#:
Cosa c'è da notare?:
Ho aggiunto il namespace a System.Data.Linq; per utilizzare le estensioni al linguaggio per Linq To SQL
Notatate che p è di tipo Products, cioè una classe con un tipo ben preciso, quello che abbiamo visto nei paragrafi sul mappaggio, inoltre da p posso accedere non solo alla proprietà Category, ma anche al nome della categoria stessa! Se andiamo a vedere la tabella Products sul database ci accorgiamo che contiene solo la colonna CategoryID che in relazione con la chiave primaria della tabella Categories. E' proprio in questa tabella che si trova la colonna CategoryName.
L'output dell'applicazione è il seguente: la lista dei prodotti della categoria cercata:
in VB.NET:
la cui logica è equivalente a quella vista in precedenza.
Che codice SQL viene generato e quando ?
Penso di prevedere una vostra domanda, anzi due.
Per vedere il codice generato, inseriamo la seguente riga di codice e rieseguiamo l'applicazione:
Come vediamo ora nella console application viene mostrato il codice SQL che viene eseguito in SQL Server:
Come vedete viene eseguita una prima chiamata SELECT in LEFT OUTER JOIN tra la tabella Products e la tabella Categories, questo perchè è solo in quest'ultima tabella che troviamo il nome della categoria. Ricordate quanto è stato semplice con Linq scrivere p.Category.CategoryName, senza preoccuparci del Join tra le tabelle. Il modello ad oggetti offerto forniva già la possibilità di navigare in relazioni 1 a molti. La seconda query è stata generata perchè abbiamo voluto avere il nome della categoria nel ciclo for each. Operazione che certamente potevamo evitare, almeno in questo semplice esempio.
Quando viene eseguita la query sul database? Se mettiamo un break-point (F9) sul cliclo foreach nell'esempio di codice, potremmo essere sorpresi di una cosa. Se guardiamo infatti la console application non vediamo ancora visualizzato il codice di esecuzione su SQL. Questo perchè non è ancora stato eseguito! Infatti nostante ci potremmo aspettare che il codice Linq viene immediatamente tradotto in codice SQL standard, non è così. Premiamo F10 un po' divolte e ci accorgiamo che il codice viene eseguito quando serve, cioè durante l'enumerazione del ciclo. Linq utilizza un modello di esecuzione differtita (deferred execution). Questo perchè LINQ analizza le operazioni che fino a quel momento sono state eseguite prima di comporre la query. In certi scenari questo comportamento è sicuramente desiderabile, in altri sarebbe desiderabile l'opposto. E quindi possibile eseguire la query una volta e riutilizzarla successivamente. Per fare questo è sufficiente convertire il risultato della query in una collezione standard usando gli operatori ToList() o ToArray() .
In breve ...
In questo primo post abbiamo visto come, in pochi semplici passi, possiamo trarre beneficio da Linq to SQL e dal O/R (Object Relational) Designer di VS 2008. L'uso di quest'ultimo è sicuramente di aiuto nello sviluppo di applicazioni RAD e in alcuni scenari non riechiede di scrivere codice ulteriore a quello già generato. Tuttavia nei prossimi post vedremo come personalizzare il codice gerato per adattarlo agli scenari di altri tipi di applicazioni.
Linq to SQL ci consente di accedere ai dati in modo diverso da quello fatto tradizionalmente con ADO.NET, realizzando uno strato di software per l'accesso ai dati, che ci consente di mappare uno a uno classi e tabelle di un database SQL e di lavorare a livello applicativo concentrandoci solo sugli oggetti senza preoccuparci troppo della struttura del nostro database SQL Server.
Nelle prossime "puntate":
Nei prossimi post vedremo come fare operazioni di scrittura sul database e come estendere e modificare il codice generato in automatico.
Link utili:
In questo articolo LINQ to SQL viene presentato in modo chiaro e completo.
LINQ to SQL: .NET Language-Integrated Query for Relational Data (articolo MSDN in inglese)
Percorso:
Per chi non ha seguito passo-passo consiglio il seguente percorso con laboratori:
Ciao e alla prossima.
-Pietro
Comments
Anonymous
September 14, 2007
PingBack from http://blogs.msdn.com/pietrobr/archive/2007/09/07/parliamo-di-linq-parte-3.aspxAnonymous
September 14, 2007
LINQ rappresenta una delle novità più importanti per lo sviluppo nella prossima release di Visual Studio.Anonymous
April 17, 2008
Scusa, ma quando modifico i dati, come faccio a non duplicare i dati degli oggetti colegati? Ad esempio, se salvo una fattura, come faccio ad indicare che non voglio salvare un cliente nuovo ad ogni salvataggio della testata della fattura? Ho un problema simile e non so come uscirne...Anonymous
April 21, 2008
Ciao Erongis, Il runtime di LINQ to SQL gestice il tracking degli oggetti creati/modificati/cancellati in modo automatico ed evita problemi come quello che a cui fai riferimento La proprietà che controlla il comportamento del Tracking è questa http://msdn2.microsoft.com/en-us/library/system.data.linq.datacontext.objecttrackingenabled.aspx -PietroAnonymous
October 17, 2008
So di essere enormemente in ritardo, ma nell'azienda cliente per cui lavoro .net 3.5 non è ancora standardizzato. Comunque: come posso agganciare da linq i risultati di una stored?Anonymous
October 17, 2008
E aggiungo: Ieri (16/10/2008) ero a firenze e mi è piaciuto parecchio.Anonymous
October 17, 2008
Cioa Rinaldo, LINQ to SQL supporta l'uso di SP e TVF, che possono essere agganciate direttamente dal designer, facendo drag & drop come fai per una tabella. Quindi nel datacontext ti trovi un metodo che punta alla store procedure.Anonymous
October 17, 2008
Cioa Rinaldo, LINQ to SQL supporta l'uso di SP e TVF, che possono essere agganciate direttamente dal designer, facendo drag & drop come fai per una tabella. Quindi nel datacontext ti trovi un metodo che punta alla store procedure.Anonymous
October 17, 2008
si il metodo l'ho trovato, ma mi torna un int invece dovrebbe essere un record set che mi popoli una data grid view