Ok, dopo il preambolo di prima, dove abbiamo illustrato
in po' di semplice teoria che sta dietro ad ADO.NET, passiamo finalmente a fare
qualcosa di concreto nella nostra applicazione Age.
In pratica, vogliamo dare all'utente la possibilità di salvare la
lista dei compleanni e successivamente di ricaricarla
. In condizioni normali dovremmo utilizzare un qualsiasi database.
Questa volta vorrei adottare un approccio diverso e più interessante: invece di
usare un database di Access o di SQL Server, salveremo le informazioni in
files XML generati direttamente senza grossi problemi da
ADO.NET.
Come ho detto nel post precedente, vogliamo utilizzare la modalità
disconnessa di ADO.NET.
Quindi, andremo a creare in
memoria una complessa struttura dati
(DataSet). Questa struttura dati può contenere una
o più tabelle (DataTable): a noi ne serve una soltanto, ovvero
la tabella che conterrà l'elenco dei compleanni inseriti e correntemente
visualizzati nella ListBox della Windows Form (WF). Ogni DataTable contiene uno
o più oggetti DataRow, che non sono nient'altro che i records
della nostra tabella.
La struttura della tabella è formata da tanti oggetti
DataColumn, che sono i campi (fields) della nostra
tabella. Quali campi servono nel nostro caso? Beh, semplice, basta dare
un'occhiata alla classe Age per capirlo. Avremo un campo
Name (stringa) e un campo BirthDate (DateTime). Ne
aggiungeremo un altro via codice per divertirci un po'.
Come si traduce tutto questo in .NET?
Beh, ormai la
sintassi di C# ci è familiare, per cui...ecco un po' di codice.
private DataTable getSaveTable(bool WithData)
{
DataRow dr;
DataColumn dtField;
// creo l'istanza dtBirthdays che ritornerò al chiamante
DataTable dtBirthdays = new DataTable();
dtBirthdays.CaseSensitive = false;
dtBirthdays.TableName = "ListBirthdays";
// creo le DataColumn che mi servono
// nome
dtField = new DataColumn("Name", Type.GetType("System.String"), string.Empty);
dtBirthdays.Columns.Add(dtField);
// data di nascita
dtField = new DataColumn("BirthDate", Type.GetType("System.DateTime"), string.Empty);
dtBirthdays.Columns.Add(dtField);
// oggi
dtField = new DataColumn("Today", Type.GetType("System.DateTime"), string.Empty);
dtField.DefaultValue = DateTime.Now;
dtBirthdays.Columns.Add(dtField);
// riempio il DataTable con i dati, solo se richiesto
if (WithData == true)
{
// con un foreach ciclo gli elementi contenuti nella ListBox
foreach (object item in this.lstResults.Items)
{
dr = dtBirthdays.NewRow();
Age ageTemp = (Age) item;
dr["Name"] = ageTemp.Name;
dr["BirthDate"] = ageTemp.BirthDate;
dtBirthdays.Rows.Add(dr);
}
}
// libero la memoria degli oggetti che implementano l'interfaccia IDispose
dtField.Dispose();
// ritorno la tabella
return(dtBirthdays);
}
Abbiamo creato una function getSaveTable.
Ritorna un'istanza di DataTable: se il parametro
WithData vale true,
allora la DataTable contiene anche i dati, altrimenti soltanto la
struttura. Cosa vuol dire "contiene anche i dati"? In breve, la property Rows di dtBirthdays
conterrà tanti oggetti DataRow quanti sono gli Items della ListBox
(pensiamo ad ogni DataRow come ad un record di una
tabella).
Esaminiamo riga per riga la function. Creo un'istanza di DataTable chiamata
dtBirthdays, specifico che il contenuto è case-insensitive e
gli do un nome, cioè "ListBirthdays". Attenzione: la
struttura della tabella fino a questo momento è vuota! Dato che non abbiamo un database reale, .NET non conosce i campi, perciò glielo dobbiamo
specificare noi. Come? La risposta è nelle righe successive.
Usando dtField, creo tanti oggetti
DataColumn. Sfruttando il costruttore della classe, li creo
indicando, nell'ordine: nome del campo, tipo
dati e una expression (nel nostro caso, sempre string.Empty). Una volta creato ciascun dtField,
lo aggiungo alla collection Columns della nostra DataTable
dtBirthdays. In questo modo, creo una piccola tabella in memoria con tre campi:
Name, BirthDate e Today. Per creare quest'ultimo ho utilizzato la property
DefaultValue per impostare un valore predefinito per i nuovi
"records" (chiamiamoli così, ma ricordiamoci che in realtà saranno
DataRow). Ogni istanza di DataColumn ha moltissime
proprietà, esploratele e vedrete! Dedicherò un post al riguardo, perchè si
possono scoprire diverse cose interessanti. Ok, a questo punto abbiamo in memoria pronta per
accogliere tutti i dati che ci servono: come la popoliamo? Vediamolo
subito...
Innanzitutto, ricordiamoci del parametro WithData: se il suo
valore è false, possiamo uscire dalla function, perchè abbiamo richiesto la creazione della
DataTable vuota, senza dati. Se vale true, proseguiamo con il codice. Con l'implementazione del ciclo foreach, ciclo tutti gli Items della ListBox. Riporto qui sotto il codice
per maggiore
chiarezza:
foreach (object item in this.lstResults.Items)
{
dr = dtBirthdays.NewRow();
Age ageTemp = (Age) item;
dr["Name"] = ageTemp.Name;
dr["BirthDate"] = ageTemp.BirthDate;
dtBirthdays.Rows.Add(dr);
}
Ricordiamoci quello che abbiamo fatto per aggiungere un Item
alla ListBox: la nostra classe Age fa l'overloading del metodo
ToString(): questo vuol dire che ogni Item è anche un'istanza
di Age. Questo è il motivo per cui il casting (Age)
item non dà alcun problema. Con una sola
istruzione, possiamo accedere alle property Name e BirthDate dell'Item
corrispondente. Vediamo come popolare adesso la nostra DataTable.
dr è una nuova istanza di DataRow (che possiamo assimilare come un nuovo record) che viene
restituita dalla chiamata del metodo NewRow() della nostra
DataTable. La cosa interessante è che .NET in questo caso ci aiuta:
dr è una vera e propria tabella, che contiene le DataColumn che
abbiamo creato prima: ogni DataColumn ha un suo nome, perciò possiamo
tranquillamente scrivere dr["Name"] = "Nome della persona".
Notate l'uso delle parentesi quadre: la sintassi ci dice che .NET sta
manipolando array in memoria. Una volta valorizzati i due campi (accedendo
tramite ageTemp), aggiungo la DataRow a dtBirthdays.
Tutto in memoria. Una volta terminato il ciclo foreach, chiamo
la Dispose() sugli oggetti temporanei che ho utilizzato e
ritorno al chiamante la DataTable che ho creato.
Questa function è la struttura portante delle nuove
features che aggiungeremo alla nostra applicazione. Provate a ritornare
all'inizio di questo post: vi avevo parlato di una complessa struttura
dati chiamata DataSet. Non l'abbiamo ancora utilizzata, non vi pare?
Questo sarà tema dei prossimi post, nei quali vedremo come creare un DataSet,
come aggiungere la DataTable che abbiamo visto adesso e cosa possiamo fare con
il DataSet, che, come ho detto prima, è il vero fulcro del discorso.
Alla prossima!