I DataSet in ADO.NET
a cura di Antonio Catucci (requisiti: Conoscenza programmazione orientata agli oggetti)

Premessa
Tra le tante novità introdotte con la nuova piattaforma di sviluppo .NET, troviamo quella relativa alla gestione dei dati. Si tratta della tecnologia chiamata ADO.NET.
Le classi per la gestione dei dati si trovano in una serie di namespaces appartenenti al namespace principale System.Data all’interno del Framework.NET. In questo articolo prenderemo in considerazione l’oggetto DataSet.

L’oggetto DataSet
L’oggetto DataSet è un contenitore di dati disconnesso, cioè indipendente dalla fonte dati, che include le tabelle in cui i dati sono contenuti.
Ciascuna tabella viene memorizzata nell’oggetto DataTable, referenziata dalla proprietà Tables, mentre ciascun record è rappresentato dall’oggetto DataRow, referenziato dalla proprietà Rows della DataTable.
Visto che un DataSet non è collegato a nessuna fonte dati, come facciamo a caricarci dei dati? Attraverso l’oggetto DataAdapter. Il DataAdapter è sostanzialmente l’oggetto che permette di far comunicare una fonte dati con un DataSet.
In funzione del tipo di Provider utilizzato per l’accesso ai dati, avremo l’oggetto SqlDataAdapter per database SQL Server 7.0 o versioni successive e l’oggetto OleDbDataAdapter per tutti i Provider OLEDB (negli esempi useremo quest’ultimo).
Per chi volesse utilizzare ODBC è possibile scaricare separatamente la libreria completa (ODBC.NET) dal sito della Microsoft, in questo caso l’oggetto DataAdapter sarà OdbcDataAdapter.

Supponiamo di voler gestire una tabella dati consentendo le operazioni di inserimento, modifica e cancellazione. Volendo schematizzare il tipico utilizzo di un DataSet, avremo i seguenti passi:

  1. Inserimento dei dati provenienti da una fonte dati nell’oggetto DataTable, tramite un DataAdapter;
  2. Modifica dei dati attraverso gli oggetti DataRow;
  3. Aggiornamento della fonte dati con il metodo Update del DataAdapter;
  4. Aggiornamento del DataSet con il suo metodo AcceptChanges.

Analizziamo ciascuna fase singolarmente.

FASE 1: Inserimento dati nel DataSet
L’oggetto OleDbDataAdapter mette a disposizione quattro proprietà che definiscono come manipolare i dati presenti nel nostro database: SelectCommand, InsertCommand, UpdateCommand e DeleteCommand. Ciascuna di queste proprietà rappresenta un riferimento a un oggetto OleDbCommand. Dunque, per ciascun tipo di operazione che intendiamo eseguire sui nostri dati, dovremo definire il comando SQL appropriato. Definiamo un comando per la selezione dei dati:

  ' Impostazione della connessione al Database.
  Dim strConn As String
  strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
  "Data Source = C:\Northwind.mdb"
Dim cn As New OleDbConnection(strConn) ' Comando SQL per il recupero dei record. Dim sql As String sql = "SELECT IDCliente, NomeSocieta, Paese, Telefono" sql = sql & " FROM Clienti ORDER BY NomeSocieta"
Dim cmd = New OleDbCommand(sql, cn) ' Assegnamo il comando al DataAdapter. Dim da As New OleDbDataAdapter() da.SelectCommand = cmd ' Apriamo la connessione. cn.Open()

Avrete notato la possibilità di istanziare e definire gli oggetti OleDbConnection e OleDbCommand con singole istruzioni.
Ora possiamo riempire il nostro DataSet attraverso il metodo Fill dell’oggetto DataAdapter:

 Dim ds As New DataSet("dsClienti")   ' Il nome del DataSet è opzionale
' Specifichiamo al DataAdapter di copiare, oltre ai dati, anche ' le informazioni relative alle chiavi definite nella tabella da.MissingSchemaAction = MissingSchemaAction.AddWithKey
' Riempiamo il DataSet specificando il nome della tabella. ds.Clear() da.Fill(ds, "Clienti")

A questo punto il DataSet contiene i dati risultanti dall’esecuzione del comando contenuto nella proprietà SelectCommand del DataAdapter.

FASE 2: Modifica dei dati
A differenza dell’oggetto Recordset di ADO un DataSet non ha metodi per lo spostamento tra record e quindi non esiste più il concetto di posizione corrente o di segnalibro (bookmark); questo perché il DataSet è disconnesso dal database. Quindi faremo riferimento ad un record mediante l’insieme DataRowCollection referenziato nella proprietà Rows dell’oggetto DataTable. Vediamo come possiamo manipolare i nostri record:

 Dim tblClienti As DataTable()
 tblClienti = ds.Tables("Clienti") ' Referenzia la tabella Clienti contenuta nel DataSet

 ' Esempio di modifica di un record.
 tblClienti.Rows(0)("NomeSocieta") = "Cliente modificato"

 ' Esempio di inserimento nuovo record.
 Dim NewRec As DataRow
 NewRec = tblClienti.NewRow
 NewRec("IDCliente") = "XXX"
 NewRec("NomeSocieta") = "Nuovo cliente"
 tblClienti.Rows.Add(NewRec)

 ' Esempio di eliminazione record.
 tblClienti.Rows(1).Delete()
 ' oppure...
 tblClienti.Rows.Remove(1)

FASE 3: Aggiornamento della fonte dati
Dopo aver modificato i nostri dati occorre aggiornare la fonte dati in quanto, è bene ribadirlo, il DataSet è disconnesso dal database.
Per far questo dobbiamo, ancora una volta, utilizzare l’oggetto DataAdapter invocandone il metodo Update. Questo metodo non fa altro che eseguire i rispettivi comandi SQL INSERT, UPDATE e DELETE per ciascuna riga inserita, modificata o eliminata nel DataSet. Per stabilire quali sono i record modificati viene utilizzata la proprietà RowState dell’oggetto DataRow e tale valore può essere Unchanged, Modified, Added o Deleted. In funzione del tipo di modifica che è stata effettuata, il DataAdapter eseguirà il comando SQL appropriato, quindi nel caso di un record la cui proprietà RowState risulti Modified verrà eseguito il comando UPDATE contenuto nella proprietà UpdateCommand del DataAdapter, e così via per tutti gli altri.
Esistono due modi per poter generare comandi SQL: manualmente nel codice oppure automaticamente con l’oggetto OleDbCommandBuilder. Vediamoli entrambi:

 ' Generazione automatica dei comandi SQL.
 ' I comandi vengono generati sulla base di quanto contenuto nella SelectCommand
 ' che in questo caso deve essere obbligatoria.
' E' necessario specificare il DataAdapter di cui generare i comandi SQL Dim cmdBuild As New OleDbCommandBuilder(da)
' Assegniamo i comandi generati al nostro DataAdapter da.UpdateCommand = cmdBuild.GetUpdateCommand() da.DeleteCommand = cmdBuild.GetDeleteCommand()

E’ bene sottolineare il fatto che l’oggetto OleDbCommandBuilder genera comandi SQL per l’aggiornamento di una singola tabella. Quindi se ci sono più tabelle collegate tra loro bisognerà generare delle istruzioni SQL ad hoc per aggiornare il database correttamente (eventualmente si possono usare delle stored procedure personalizzate). Vediamo ora come creare un comando SQL manualmente:

 ' Esempio di creazione manuale di un comando SQL
 Dim sql As String
 sql = "INSERT INTO Clienti (IDCliente, NomeSocieta, Paese, Telefono)"
 sql = sql & " VALUES(@IDCliente, @NomeSocieta, @Paese, @Telefono)"
Dim cmd As New OleDbCommand(sql, cn)
da.InsertCommand = cmd da.InsertCommand.Parameters.Add("@IDCliente", OleDbType.VarChar, 5, "IDCliente") da.InsertCommand.Parameters.Add("@NomeSocieta", OleDbType.VarChar, 40, "NomeSocieta") da.InsertCommand.Parameters.Add("@Paese", OleDbType.VarChar, 15, "Paese") da.InsertCommand.Parameters.Add("@Telefono", OleDbType.VarChar, 24, "Telefono")

Nel comando SQL i campi che iniziano con @ rappresentano i parametri che il DataAdapter sostituirà con i valori corrispondenti presenti nel DataSet. E’ possibile utilizzare anche il carattere ‘?’ come segnaposto, ma per maggiore chiarezza è preferibile usare un nome descrittivo. Attraverso la Collection OleDbParameterCollection, referenziata nella proprietà Parameters dell'oggetto OleDbCommand, sono stati aggiunti tutti i parametri necessari per l’esecuzione del comando SQL specificando il nome del parametro, il tipo, la dimensione e il nome della colonna di origine mappata con la corrispondente nell'oggetto DataSet. A questo punto il DataAdapter ha tutti i comandi necessari per l’aggiornamento della fonte dati che verrà fatto chiamando il metodo Update:

 ' Il metodo Update() restituisce il numero dei record
 ' interessati dall’aggiornamento.
 da.Update(ds, "Clienti")

FASE 4: Aggiornamento del DataSet
Se l’operazione di aggiornamento della fonte dati termina con successo possiamo confermare le modifiche anche nel DataSet con il metodo AcceptChanges, altrimenti per ripristinare il tutto sarà sufficiente invocare il metodo RejectChanges:

 ds.AcceptChanges()

Il metodo AcceptChanges reimposta lo stato di tutti i record riportandolo al valore Unchanged, quindi la chiamata del metodo Update ha effetto su tutti i record che risultano modificati dall’ultima chiamata del metodo AcceptChanges o dal caricamento del DataSet.

Conclusione
In questo articolo abbiamo visto come è possibile gestire i dati con i DataSet. In realtà quest’oggetto è molto più potente di quanto si possa immaginare. Negli esempi è stato considerato l’esempio più semplice, ovvero la gestione di una sola tabella (cosa che si fa regolarmente con i Recordset di ADO), mentre è possibile gestire più tabelle contemporaneamente impostando delle relazioni tra loro. Inoltre è possibile filtrare i dati, gestire gli schemi delle tabelle, gestire i dati direttamente in XML, ecc Quindi vale davvero la pena capire il loro funzionamento.

In merito a quest'articolo, potete scrivere all'autore Antonio Catucci

L'Autore
Antonio Catucci è un giovane programmatore autodidatta. Programma in Visual Basic dalla versione 4.
Attualmente lavora presso la società Agorà Software S.r.l. allo sviluppo di applicazioni rivolte al settore tessile.