Se questo fosse un romanzo potrebbe iniziare così: "Tornando dal campus di Microsoft a Redmond, verso il mio albergo di Bellevue, nella stupenda cornice naturale di pini e larici che costeggiano la Bel-Red road, illuminati dalla luce fioca di un tramonto di mezza estate, venni assalito dalla irrefrenabile passione per la classe DataSet di ADO.NET, così fortemente che non riuscii a trattenermi dallo scrivere per tutta la notte un articolo da pubblicare sul sito di DevLeap……".
Ok, ok, questo non è un romanzo, e a dire la verità a farmi stare alzato tutta la notte ha contribuito il doppio hamburger che ho mangiato alla cafeteria nell’area denominata "Red West" (una parte del campus MS. Ogni gruppo di building infatti ha un "nickname" che gli è stato storicamente assegnato dai dipendenti), insieme ad alcuni miei ex-colleghi che ora lavorano qui, che proprio non ne voleva sapere di andare giù J. Comunque il concetto di base rimane sempre quello, e cioè: la mia folgorazione per il DataSet. Questo è un elenco di argomenti che tratteremo in questo e nei prossimi articoli di questa serie:
- Overview
- Struttura interna del DataSet
- Modello ad ogetti
- Analisi degli oggetti principali
- La DataTable
- Costruire relazioni e "navigare" tra i dati
- Utilizzo delle DataView
- Relazione tra DataSet e DataAdapter
- Definizione dei comandi del DataAdapter
- Comandi custom o CommandBuilder?
- ADO.NET Batch updates
- Gestione dei conflitti
- DataSet XML Schema e strong typed DataSet
- DataSet marshaling
Dalla sua introduzione infatti, è stato subito chiaro come ADO.NET avesse una sorta di "doppia personalità". Da un lato la sua parte "connessa" verso le diverse fonti dati per tutte le operazioni da e verso queste ultime, e dall’altra il lato "disconnesso" del suo carattere, con la possibilità di operare con informazioni che provengono da qualsiasi fonte dati, relazionale e non relazionale, senza la necessità di essere sempre e comunque in comunicazione con questa.
La classe DataSet è il vero cuore dell’ambiente disconnesso di ADO.NET, nato dalle esperienze fatte con la tecnica dei Recordset disconnessi in ADO 2.x, componenti che per la prima volta consentivano di fare operazioni sui dati senza la necessità di una connessione aperta verso la fonte dati dalla quale provenivano.
L’idea di poter avere uno strumento che permettesse agli sviluppatori di gestire una cache dei dati a disposizione dalle applicazioni, assolutamente indipendente e slegata da ogni specifica base dati, magari da utilizzare lato server e da condividere tra tutti gli utenti di un sito web o di componenti di business in una soluzione distribuita, potendo gestire gli aggiornamenti verso i vari database server, controllandone eventualmente i conflitti e la sincronizzazione, non è cosa nuova. Già qualche anno fa abbiamo sentito parlare di una tecnica del genere, del resto non nuova anche ad altre piattaforme, come uno dei servizi originariamente esposti da COM+. Il nome esatto di quella tecnica era IMDB o In-Memory DataBase. Questo servizio, presente nelle prime beta di Windows 2000 è poi stato eliminato dalle release definitive per alcuni problemi tecnici non risolti, ma anche perché in mano a sviluppatori poco ortodossi avrebbe potuto fare più danni che altro (è una mia opinione… anche se abbastanza ragionevole J ). Immaginate infatti cosa sarebbe potuto succedere? Porzioni di centinaia di megabyte di dati sarebbero state caricate senza criterio nella memoria già scarsa degli application server sperando così di risolvere i problemi architetturali delle applicazioni, riuscendo probabilmente solo ad ammazzare le prestazioni di qualsiasi macchina.
Un altro problema che non era ancora stato risolto era la necessità di gestire questo strumento di caching in modalità distribuita (ad esempio in una web farm) e soprattutto garantire meccanismi di sincronizzazione delle informazioni, verso le varie basi dati di origine, in modo efficiente e affidabile.
Il DataSet nasce in realtà con obiettivi molto meno ambiziosi, anche se mette a disposizione degli sviluppatori un insieme di funzionalità che, se combinate con altre fornite da altre parti del Framework come ad esempio l’oggetto Cache di ASP.NET, si avvicinano molto a quello che era l’idea iniziale, lasciando agli sviluppatori la possibilità di decidere la logica di aggiornamento e di gestione della concorrenza nella sincronizzazione delle informazioni verso il database.
Vediamone graficamente la struttura interna:

Fig.1 la struttura di base del DataSet
Innanzitutto diciamo che la classe DataSet è contenta nel namespace System.Data per un motivo ben preciso: essendo questa classe completamente indipendente dalle fonti dati dalle quali arrivano le informazioni in essa contenute, non avrebbe avuto senso legare la classe ad uno specifico .NET Data Provider, e quindi è stata posta in un namespace assolutamente generico.
Come possiamo facilmente intuire dalla figura, una istanza della classe DataSet è una sorta di contenitore di informazioni strutturate in collezioni. Una domanda che inizialmente mi sono posto non appena ho visto questa struttura è: "Ma non ci avete ripetuto per anni che gli oggetti per l’accesso ai dati devono essere snelli ed efficienti, e che era proprio la presenza di troppe collezioni popolate automaticamente di oggetti la causa dell’inefficienza di DAO nell’accesso a basi dati remote che ha poi portato alle evoluzioni con RDO e ADO? Torniamo di nuovo da capo?". Analizzando meglio quello che succede dietro le quinte, la risposta è sicuramente NO. Infatti, come vedremo più avanti quando parleremo di XML e dello schema del DataSet, quest’ultimo ha la capacità di adattarsi alle diverse situazioni e di popolare o meno tutta una serie di informazioni sulla base dati di origine sulla base del contesto nel quale è utilizzato, garantendo sempre la massima efficienza a seconda delle varie condizioni.
Iniziamo ovviamente dalla collezione principale e più importante del DataSet, accessibile attraverso la proprietà Tables della nostra istanza, cioè la DataTableCollection (se non sono presenti DataTable nella collezione, la proprietà Tables ha valore null ). Solo come similitudine, potremmo pensare a questa collezione con ad una serie di Recordset ADO (che nel mondo .NET sono più simili alla classe DataTable, cioè ad una struttura di tipo gabellare con N righe e M colonne) ma che vedremo possono essere messi in relazione gli uni con gli altri per sostanzialmente due obiettivi principali:
- garantire l’integrità dei dati nel DataSet anche senza un collegamento "fisso" con la base dati dalla quale provengono
- facilitare la "navigazione" tra le informazioni, mettendo a disposizione un modo sicuro ed efficace per lavorare con gerarchie di tipo master-detail o più complesse
Proprio per queste caratteristiche, unite anche alla possibilità di poter definire dei vincoli (constraint) sulle informazioni immesse all’interno del DataSet, e alla possibilità di definire delle viste logiche sui dati, fare ordinamenti e filtrare le informazioni, l’accostamento con un piccolo motore di database che risiede in memoria è più che lecito.
Vediamo come è possibile instanziare un oggetto di tipo DataSet e come aggiungere e accedere alle DataTable in esso contenute:
using System;
using System.Data; // namespace che contiene la classe DataSet
public class Test
{
public static void Main()
{
DataSet myds = new DataSet("TestDs"); // creo una nuova istanza
CreateTable(myds);
GetTables(myds);
Console.ReadLine();
}
static void GetTables(DataSet ds)
{
// per tutte le DataTable nella collection scorro il contenuto
foreach (DataTable t in ds.Tables)
foreach (DataRow r in t.Rows)
foreach (DataColumn c in t.Columns)
if (r[c] != null) Console.WriteLine(r[c]);
}
static void CreateTable(DataSet ds)
{
// creo una nuova DataTable, la popolo e la aggiungo al DataSet
DataTable newTable = new DataTable("MyTable");
newTable.Columns.Add("ID", typeof(int));
newTable.Columns.Add("Name", typeof(string));
DataRow dr = newTable.NewRow();
dr["ID"] = 1;
dr["Name"] = "Silvano";
newTable.Rows.Add(dr);
ds.Tables.Add(newTable);
}
}
Come abbiamo visto, il DataSet in questione non è stato creato accedendo ad un qualsiasi database server, ma attraverso la definizione della struttura interna di una DataTable e la sua popolazione con alcune informazioni. Questa modalità di utilizzo del DataSet ci fa capire come questo componente può essere normalmente utilizzato non solo per leggere e/o modificare le informazioni in una qualsiasi fonte dati, ma anche come struttura per rappresentare, scambiare e validare (e lo vedremo ancora meglio quando più avanti parleremo di strongly typed DataSet) le informazioni nelle nostre applicazioni .NET.
Nei prossimi giorni il seguito con gli approfondimenti... stay tuned!!
