Introduzione agli ADO.NET Data Services con Visual Basic 2008 - Prima parte: il servizio
a cura di Alessandro Del Sole (requisiti: conoscenze di base su LINQ e Windows Communication Foundation)Introduzione
Negli scenari moderni è molto frequente la necessità di creare applicazioni in grado di accedere a un database attraverso la rete, che sia essa di tipo locale (Intranet) o Internet. Spesso, infatti, molte aziende sviluppano applicazioni proprietarie che permettano agli operatori, mediante applicazioni client, di lavorare su una sorgente dati posta su un server, il tutto attraverso la rete aziendale.
Questo tipo di situazione non è sempre di semplice implementazione, soprattutto nel momento in cui si deve stabilire con quali modalità client e server devono comunicare. Nell'intento di semplificare ulteriormente la vita allo sviluppatore e agli amministratori di sistema, nonché per fornire una via unificata per l'accesso ai dati nella comunicazione client-server, .NET Framework 3.5 Service Pack 1 introduce una importantissima novità, costituita dagli ADO.NET Data Services (precedentemente noti come 'progetto Astoria').
Si tratta di un framework specifico per lavorare con i dati in rete, il tutto attraverso l'utilizzo di URI HTTP.
La comunicazione dei dati avviene attraverso servizi basati su Windows Communication Foundation e i dati stessi, rappresentati in forma di oggetti grazie all'ADO.NET Entity Framework, vengono esposti attraverso un approccio di tipo REST (REpresentational State Transfer). I client, poi, potranno raggiungere i dati semplicemente referenziando il servizio WCF e utilizzando LINQ.
Come avremo poi modo di discutere successivamente, questo tipo di infrastruttura offre tanti vantaggi, uno fra tanti è la possibilità di utilizzare lo stesso approccio in diverse tipologie di applicazioni client (Windows Forms, WPF, Console, Silverlight, Web).Nelle poche righe sopra esposte ci sono molti nuovi concetti, che affronteremo secondo una modalità step-by-step, quindi non spaventatevi se vi sembrano poco chiari.
La trattazione introduttiva agli ADO.NET Data Services in Visual Basic 2008 sarà suddivisa in due parti: in questo primo articolo tratteremo la creazione del servizio; nel secondo creeremo un'applicazione client con Windows Presentation Foundation, piuttosto scarna nell'estetica ma molto efficace e interessante dal punto di vista dell'accesso ai dati.
Il codice sorgente completo verrà reso disponibile a corredo del prossimo articolo, di modo che non abbiate la tentazione di mettere subito mano al codice senza aver prima capito cosa stiamo facendo. JPrerequisiti
Sulla macchina di sviluppo è necessario aver installato il seguente software:
- Microsoft Visual Studio 2008 Service Pack 1;
sono utilizzabili anche le edizioni Express, tuttavia sarà necessario utilizzare Visual Web Developer Express per la creazione del servizio e Visual Basic 2008 Express per la creazione dei client, mentre nel più completo Visual Studio 2008 sarà possibile creare una soluzione singola nello stesso ambiente;- Microsoft SQL Server 2005 Express;
- Internet Information Services;
per la presente dimostrazione si fa utilizzo della versione 7, inclusa in Windows Vista e Windows Server 2008, ma è possibile utilizzare anche la versione 6.0.- Database dimostrativo Northwind.
Poichè gli ADO.NET Data Services basano il loro funzionamento su Windows Communication Foundation, è altresì necessario assicurarsi che i componenti di questa piattaforma siano abilitati nel sistema, attraverso la gestione dei componenti di Windows raggiungibile dal Pannello di Controllo/Programmi e funzionalità.
Cosa vuol dire interrogare i dati via URI HTTP?
In .NET Framework 3.5, Windows Communication Foundation è in grado di supportare la serializzazione dei dati secondo la modalità REST.
In parole molto povere, si tratta di un tipo di serializzazione XML che permette, tra l'altro, oltre alla standardizzazione dello scambio dei dati, di interrogare i dati stessi tramite URI, ossia un indirizzo Web che digitiamo nella barra degli indirizzi del nostro browser.
Per fare un sintetico esempio, se ipotizziamo di avere un servizio WCF che espone i dati del database Northwind, potremmo pensare di ottenere, in forma XML, i dati esposti nella sua tabella Customers digitando il seguente URI nella barra di Internet Explorer:http://localhost/NorthwindDataService.svc/CustomersIl risultato di questa digitazione sarà la visualizzazione nella finestra del browser di una pagina di codice XML rapprentativo di tutti i dati della predetta tabella. Possiamo anche ipotizzare di voler ottenere le informazioni relative agli ordini effettuati da un singolo cliente, ordinandoli per data, come nella seguente riga:
http://localhost/NorthwindDataService.svc/Customers(‘ALFKI')/Orders?orderby=OrderDateCome potete vedere, si possono scrivere query (anche complesse) direttamente tramite URI ed esiste tutta una simbologia per fare questo. I client potranno accedere ai dati e interrogarli tipicamente secondo due modalità. La prima, tramite operazioni c.d. in querystring:
Dim Dati = Northwind.Execute(Of Orders)(New Uri("/Customers('ALFKI')/Orders?orderby=OrderDate", _ UriKind.Relative))La seconda, utilizzando una speciale implementazione di LINQ chiamata LINQ to DataServices che, in realtà, trova la sua implementazione di base in LINQ to Entities:
Dim Categorie = From cat In Northwind.Categories _ Order By cat.CategoryID _ Select cat.CategoryNameTutto questo sarà comunque oggetto di approfondimento nel prossimo articolo dedicato ai client. Possiamo quindi generalizzare il discorso rappresentando la sintassi che si utilizza per interrogare i dati via HTTP, secondo la terminologia proprie dell'ADO.NET Entity Framework:
http://sito/Servizio/EntitySet/NavigationPropertyCome avviene l'esposizione dei dati
I dati vengono trasmessi dal server ai client secondo un'architettura a layer. Alla base, c'è la sorgente dati, quindi il database. Al livello superiore c'è l'ADO.NET Entity Framework che espone i dati in forma di oggetto di tipo IQueryable. Questi oggetti vengono agganciati, al livello superiore, da una classe chiamata DataService, che vedremo in seguito, che si occupa di esporre ai client i dati.
Questo, in sostanza, è tutto il lavoro svolto dal servizio WCF. Il bello è che per fare tutto questo, grazie a Microsoft Visual Studio 2008, scriveremo pochissime righe di codice.Dopo una breve introduzione teorica, passiamo subito alla creazione del servizio con Visual Studio durante la quale approfondiremo alcuni passaggi.
Creazione del servizio di esempio
Una volta avviato Visual Studio 2008, create una nuova soluzione vuota, utilizzando il modello chiamato Blank Solution presente nella cartella Other project types/Visual Studio Solutions, assegnando alla soluzione il nome NorthwindDataServices come in figura:![]()
Fatto questo, facendo clic destro sul nome della soluzione in Solution Explorer, selezionate il comando Add/New project del menu contestuale. Aggiungete alla soluzione un progetto di tipo Web Application in Visual Basic, chiamato NorthwindService come in figura:
![]()
Dopo aver creato il progetto, passiamo all'aggiunta dell'origine dati e dell'Entity Data Model.
Aggiunta della sorgente dati
Facciamo clic destro sulla cartella App_Data e selezioniamo il comando Add/Existing item, al fine di reperire e selezionare il database Northwind.mdf. Una volta selezionato, il database viene aggiunto al progetto. Come accennato all'inizio dell'articolo, gli ADO.NET Data Services sfruttano ADO.NET Entity Framework per la gestione orientata agli oggetti dei dati contenuti nel database. Questo è uno dei vari vantaggi cui si faceva cenno in precedenza, nel senso che l'Entity Framework permette, anche tramite provider esterni, di interfacciarsi a database diversi da SQL Server ma esponendo i dati in maniera analoga. Ciò premesso, tramite il comando Project/Add new item, selezioniamo la cartella Data quindi l'elemento ADO.NET Entity Data Model, assegnando il nome Northwind.edmx come in figura:![]()
Dopo aver fatto clic su Add, viene avviata la procedura guidata per l'aggiunta dell'Entity Data Model, ossia il modello a oggetti che mapperà sotto forma di entità le tabelle del database. Nella prima schermata del wizard specificheremo che il modello dovrà essere creato a partire da un database esistente. Nella seconda, dovremo accertarci che la stringa di connessione punti al database memorizzato precedentemente nella cartella App_Data. Il tutto dovrebbe presentarsi in modo simile al seguente screenshot:
![]()
Nella schermata seguente, invece, selezioniamo tutte le tabelle come in figura e terminiamo la procedura guidata:
![]()
Aggiunta del servizio WCF
Il passaggio successivo consiste nell'aggiungere al progetto un servizio WCF, abilitato al trattamento dei dati secondo l'approccio REST. A differenza di quanto dovremmo fare in altri scenari, lavorando coi Data Services non dobbiamo scrivere codice per fare questo tipo di abilitazione, poiché il team di sviluppo di questo framework ha previsto uno specifico item template per la creazione di un servizio già abilitato e specifico per i Data Services. Detto questo, selezioniamo il comando Project/Add new item e spostiamoci nella cartella Web, dove troveremo un modello chiamato ADO.NET Data Service, che chiameremo Northwind.svc come in figura:![]()
Visual Studio crea così un file Northwind.svc, costituito da codice di mark-up col quale avrete sicuramente confidenza se avete quantomeno iniziato a studiare Windows Communication Foundation:
<%@ ServiceHost="" Language="VB" Factory="System.Data.Services.DataServiceHostFactory, System.Data.Services, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Service="NorthwindService.Northwind" %>Oltre ai riferimenti, questo codice (mandato a capo per ragioni di impaginazione) ha di interessante la specifica della classe che gestisce il funzionamento del servizio e che, nel nostro caso, è chiamata Northwind. Tale classe è definita nel file Northwind.svc.vb (potete raggiungerla abilitando la visualizzazione di tutti i file o semplicemente selezionando la visualizzazione del codice sul file Northwind.svc) in questo modo:
Imports System.Data.Services Imports System.Linq Imports System.ServiceModel.Web Public Class Northwind ' TODO: replace [[class name]] with your data class name Inherits DataService(Of [[class name]]) ' This method is called only once to initialize service-wide policies. Public Shared Sub InitializeService(ByVal config As IDataServiceConfiguration) ' TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc. ' Examples: ' config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead) ' config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All) End Sub End ClassLa classe Northwind eredita da DataService(Of T), una classe che rappresenta il punto di ingresso per i servizi ADO.NET. Come vedremo nel prossimo paragrafo, il metodo InitializeService consentirà di specificare le autorizzazioni per l'accesso alle entità. Il tipo T specificato nell'ereditarietà è costituito dall'oggetto generato dall'Entity Data Model per accedere alle entità.
Modifica della classe DataService(Of T)
Una volta che abbiamo davanti il codice della classe, dobbiamo modificare la sua dichiarazione di ereditarietà in questo modo:Inherits DataService(Of NORTHWNDEntities)NORTHWNDEntities costituisce il contesto (ObjectContext) definito dall'Entity Data Model e col quale ci si rapporta per eseguire le operazioni sulle entità, oltre ad essere l'oggetto che ne gestisce il ciclo di vita.
La successiva modifica riguarda la concessione di autorizzazioni ai client sulle entità. In primo luogo, decommentate la seguente riga:config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead)Config è un oggetto di tipo IDataServiceConfiguration e si occupa di gestire le autorizzazioni nell'ambito del servizio. Il metodo SetEntityAccessRule stabilisce quali autorizzazioni devono avere le entità specificate. È possibile specificare singole entità oppure tutte le entità. Per esempio, la seguente riga assegna all'entità Customers la sola autorizzazione in lettura:
config.SetEntitySetAccessRule("Customers", EntitySetRights.AllRead)Sebbene in uno scenario reale sia sempre importante avere un quadro ben chiaro delle autorizzazioni e che non sia opportuno concedere accesso indiscriminato su tutti i dati, per fini assolutamente didattici e di praticità modifichiamo la riga sopra esposta come segue:
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead)L'enumerazione EntitySetRigthts permette di specificare il livello autorizzativo e l'IntelliSense vi aiuterà a capire i vari livelli. L'utilizzo del simbolo * specifica che le autorizzazioni indicate sono assegnate a tutte le entità. Nel nostro codice, stiamo assegnando autorizzazioni di creazione, lettura, modifica ed eliminazione dei dati verso tutte le entità.
Lasciamo invece commentata la riga successiva, relativa al metodo SetServiceOperationAccessRule, poiché questo si riferisce all'implementazione di operazioni sul servizio personalizzate, che non verranno trattate in questa sede, ma che costituiscono una importante possibilità di eseguire operazioni sui dati in modalità server-side. Dopo queste semplici modifiche, la predisposizione del nostro Data Service è terminata e siamo pronti per mandarlo in esecuzione.
Test del servizio
Premete F5 per compilare la soluzione ed avviare il servizio, accettando la richiesta di modifica del file Web.config per l'abilitazione del debugging. Come probabilmente sapete, tutti i servizi WCF devono essere ospitati in un'applicazione definita host che, durante la fase di sviluppo, è costituita dal server ASP.NET. Se tutto è andato a buon fine, il browser si presenta come in figura:![]()
Come potete osservare, il risultato consiste in una rappresentazione XML delle varie entità mappate nell'Entity Data Model, quindi provenienti dalle tabelle del database. La rappresentazione XML è avvenuta mediante REST.
Ora, provate a modificare il contenuto della barra degli indirizzi con questo URI:http://localhost:2948/Northwind.svc/CustomersIl risultato a video sarà il seguente:
![]()
Saranno così mostrati i dettagli di tutti i clienti (Customers) definiti nell'entità. In sostanza, il nome dell'entità confluisce nell'URI e questo ci permetterà di ottenerne i dettagli. Nella schermata, rimpicciolita per motivi di spazio, si possono osservare i dettagli del cliente contraddistinto dall'identificativo ALFKI.
A questo punto proviamo a fare un'interrogazione un po' più complessa, digitando il seguente URI:
http://localhost:2948/Northwind.svc/Customers(‘ALFKI')/Orders?orderby=OrderDateche produrrà il seguente risultato:
![]()
Tramite l'interrogazione formulata direttamente nell'URI abbiamo chiesto al servizio di restituire tutti gli ordini eseguiti dal cliente ALFKI, ordinandoli per data. Questo tipo di serializzazione è in grado anche di stabilire le relazioni tra le varie entità.
Come detto, l'utilizzo degli URI ci serve più che altro per capire come il servizio WCF esponga i dati e non tanto per un utilizzo vero e proprio. Infatti, nel corso del prossimo articolo, allorquando ci occuperemo di creare un client, faremo prevalentemente utilizzo di LINQ to DataServices anche se accennermo alla possibilità di eseguire interrogazioni in querystring da codice Visual Basic.
Hosting del servizio
In questa mini serie di articoli non verrà trattato il discorso del deploy del servizio, poiché la dimostrazione viene eseguita in locale, sebbene non lo si escluda per il futuro. Ad ogni buon conto giova precisare che, poiché si tratta di un servizio WCF a tutti gli effetti, la sua distribuzione avviene secondo le consuete modalità previste per questo tipo di servizi, riassunte in questa pagina della documentazione MSDN.Conclusioni
In questo primo articolo dedicato agli ADO.NET Data Services abbiamo iniziato a vedere quale sforzo sia stato fatto per offrire un sistema unificato per l'esposizione dei dati in rete e per il relativo consumo attraverso .NET Framework, percorso che risulterà ancor più interessante nella seconda parte dove LINQ completerà il processo di unificazione.
Per ulteriori informazioni potete consultare la documentazione MSDN ufficiale mentre, per commenti e quant'altro di vostro interesse, potete contattarmi al mio indirizzo visitare il mio blog.