Aggiornamento del database con un oggetto DataAdapter e il DataSet

Il metodo Update di DataAdapter viene chiamato per applicare le modifiche apportate a un DataSet nell'origine dati. Il metodo Update, analogamente al metodo Fill, accetta come argomenti un'istanza di un DataSet e un oggetto DataTable facoltativo o il nome DataTable. L'istanza DataSet rappresenta il DataSet contenente le modifiche che sono state apportate e il DataTable identifica la tabella da cui recuperare le modifiche.

Quando si chiama il metodo Update, DataAdapter analizza le modifiche che sono state apportate ed esegue il comando appropriato (INSERT, UPDATE o DELETE). Quando DataAdapter rileva una modifica a un DataRow, elabora la modifica utilizzando InsertCommand, UpdateCommand o DeleteCommand. In questo modo è possibile ottimizzare le prestazioni dell'applicazione ADO.NET specificando la sintassi del comando in fase di progettazione e, dove possibile, mediante stored procedure. È necessario impostare in modo esplicito i comandi prima di chiamare Update. Se Update viene chiamato e non esiste il comando appropriato per un determinato aggiornamento, ad esempio nessun DeleteCommand per le righe cancellate, viene generata un'eccezione.

È possibile utilizzare i parametri Command per specificare i valori di input e di output di un'istruzione SQL o una stored procedure per ogni riga modificata in un DataSet. Per ulteriori informazioni, vedere Utilizzo di parametri con un DataAdapter.

Se DataTable esegue il mapping o viene generato da una singola tabella di database, è possibile utilizzare l'oggetto CommandBuilder per generare automaticamente DeleteCommand, InsertCommand e UpdateCommand di DataAdapter. Per ulteriori informazioni, vedere Comandi generati automaticamente.

Il metodo Update applica le modifiche nell'origine dati, ma è possibile che altri client abbiano modificato i dati nell'origine dati dall'ultima volta che è stato riempito il DataSet. Per aggiornare il DataSet con i dati correnti, utilizzare DataAdapter ed eseguire di nuovo l'operazione Fill sul DataSet. Le nuove righe verranno aggiunte alla tabella e le informazioni aggiornate verranno inserite nelle righe esistenti. Quando si esegue il metodo Fill, verrà determinato se aggiungere una nuova riga o aggiornare una riga esistente esaminando i valori delle chiavi primarie delle righe del DataSet e delle righe restituite dal SelectCommand. Se con l'esecuzione del metodo Fill viene trovato un valore di chiave primaria di una riga del DataSet che corrisponde al valore di chiave primaria di una riga presente nei risultati restituiti dal SelectCommand, la riga esistente verrà aggiornata con le informazioni presenti nella riga restituita dal SelectCommand e il RowState della riga esistente verrà impostato su Unchanged. Se una riga restituita dal SelectCommand presenta un valore di chiave primaria che non corrisponde ad alcuno dei valori di chiave primaria delle righe del DataSet, l'esecuzione del metodo Fill comporterà l'aggiunta di una nuova riga con RowState Unchanged.

Nota   Se SelectCommand restituisce i risultati di un OUTER JOIN, il DataAdapter non imposterà un valore PrimaryKey per il DataTable risultante. Per assicurarsi che le righe duplicate vengano risolte correttamente, sarà necessario definire la PrimaryKey autonomamente. Per ulteriori informazioni, vedere Definizione di una chiave primaria per una tabella.

Per gestire le eccezioni che possono verificarsi durante un Update, è possibile utilizzare l'evento RowUpdated, per rispondere agli errori di aggiornamento delle righe nel momento in cui si verificano (vedere Utilizzo di eventi DataAdapater), oppure impostare DataAdapter.ContinueUpdateOnError su true prima di chiamare Update e rispondere alle informazioni sugli errori, memorizzate nella proprietà RowError di una particolare riga, al termine dell'Update (vedere Aggiunta e lettura di informazioni sugli errori delle righe).

**Nota   **Se si chiama AcceptChanges sul DataSet, il DataTable o il DataRow, tutti i valori Original di un DataRow vengono sovrascritti con i valori Current del DataRow. Se sono stati modificati i valori dei campi che identificano la riga come univoca, dopo aver chiamato AcceptChanges i valori Original non corrisponderanno più ai valori nell'origine dati.

Negli esempi seguenti viene illustrato come aggiornare le righe modificate impostando in modo esplicito UpdateCommand di DataAdapter. Si osservi che il parametro specificato nella clausola WHERE dell'istruzione UPDATE viene impostato in modo da utilizzare il valore Original di SourceColumn. Questa operazione è importante in quanto è possibile che il valore Current sia stato modificato e che non corrisponda più al valore nell'origine dati. Il valore Original è il valore che è stato utilizzato per compilare DataTable dall'origine dati.

SqlClient

Dim catDA As SqlDataAdapter = New SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)

catDA.UpdateCommand = New SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " & _
                                     "WHERE CategoryID = @CategoryID", nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

Dim workParm As SqlParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")  

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"

catDA.Update(catDS)
[C#]
SqlDataAdapter catDA = new SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);       

catDA.UpdateCommand = new SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " +
                                     "WHERE CategoryID = @CategoryID" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");

SqlParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");   

DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";

catDA.Update(catDS);

OleDb

Dim catDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)       

catDA.UpdateCommand = New OleDbCommand("UPDATE Categories SET CategoryName = ? " & _
                                       "WHERE CategoryID = ?" , nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName")

Dim workParm As OleDbParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")    

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"

catDA.Update(catDS)
[C#]
OleDbDataAdapter catDA = new OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);            

catDA.UpdateCommand = new OleDbCommand("UPDATE Categories SET CategoryName = ? " +
                                       "WHERE CategoryID = ?" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName");

OleDbParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");    

DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
catDA.Update(catDS);

Odbc

Dim catDA As OdbcDataAdapter = New OdbcDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)

catDA.UpdateCommand = New OdbcCommand("UPDATE Categories SET CategoryName = ? " & _
                                      "WHERE CategoryID = ?" , nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", OdbcType.VarChar, 15, "CategoryName")

Dim workParm As OdbcParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OdbcType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")    

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)

cRow("CategoryName") = "New Category"

Dim modRows() As DataRow = catDS.Tables("Categories").Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent)

catDA.Update(modRows)
[C#]
OdbcDataAdapter catDA = new OdbcDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);

catDA.UpdateCommand = new OdbcCommand("UPDATE Categories SET CategoryName = ? " +
                                      "WHERE CategoryID = ?" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", OdbcType.VarChar, 15, "CategoryName");

OdbcParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OdbcType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");    

DataRow cRow = catDS.Tables["Categories"].Rows[0];

cRow["CategoryName"] = "New Category";

DataRow[] modRows = catDS.Tables["Categories"].Select(null, null, DataViewRowState.ModifiedCurrent);
catDA.Update(modRows);

Colonne AutoIncrement

Se nelle tabelle dell'origine dati sono presenti colonne con incremento automatico, è possibile riempire le colonne nel DataSet con i valori generati dall'origine dati restituendo il valore dell'incremento automatico come un parametro di output di una stored procedure ed eseguendone il mapping su una colonna della tabella o utilizzando l'evento RowUpdated di DataAdapter. Per un esempio, vedere Recupero dei valori di identità o del contatore.

I valori del DataSet possono però risultare non sincronizzati con i valori nell'origine dati, provocando un comportamento imprevisto. Si consideri, ad esempio, una tabella con una colonna CustomerID di chiave primaria con incremento automatico. Se si aggiungono due nuovi clienti nel DataSet, questi ricevono i valori CustomerId incrementati automaticamente di 1 e 2. Quando la riga del secondo cliente viene passata al metodo Update di DataAdapter, la riga appena aggiunta riceve il valore CustomerID incrementato automaticamente di 1 nell'origine dati, che non corrisponde al valore 2 nel DataSet. Quando DataAdapter riempie la riga nel DataSet con il valore restituito, si verifica una violazione dei vincoli perché la riga del primo cliente ha già un CustomerID uguale 1.

Per evitare questo comportamento, quando si utilizzano colonne con incremento automatico nell'origine dati e colonne con incremento automatico in un DataSet, è consigliabile creare la colonna nel DataSet con un AutoIncrementStep di -1 e un AutoIncrementSeed di 0 e assicurarsi che l'origine dati generi valori di identità con incremento automatico iniziando da 1 e con valori di incremento positivi. In questo modo, il DataSet genera numeri negativi per i valori incrementati automaticamente che non entrano in conflitto con i valori positivi incrementati automaticamente generati dell'origine dati. Un'altra possibilità consiste nell'utilizzare colonne di tipo Guid anziché colonne con incremento automatico. L'algoritmo che genera i valori Guid non deve mai generare nel DataSet lo stesso valore Guid generato dall'origine dati. Per ulteriori informazioni sulla definizione delle colonne in un DataTable, vedere Definizione dello schema di un DataTable.

Ordine di Insert, Update e Delete

In molti casi, l'ordine in cui le modifiche apportate mediante il DataSet vengono inviate all'origine dati è importante. Se, ad esempio, il valore di una chiave primaria relativo a una riga esistente viene aggiornato e una nuova riga con il nuovo valore della chiave primaria è stata aggiunta, è importante elaborare l'aggiornamento prima di effettuare l'inserimento.

È possibile utilizzare il metodo Select del DataTable per restituire una matrice DataRow che faccia riferimento solo a righe con un particolare RowState. È possibile quindi passare la matrice DataRow restituita al metodo Update di DataAdapter per elaborare le righe modificate. Se si specifica un sottoinsieme di righe da aggiornare, è possibile controllare l'ordine in cui gli inserimenti, gli aggiornamenti e le eliminazioni vengono elaborate.

Nel codice seguente, ad esempio, viene impostata prima l'elaborazione delle righe della tabella cancellate, poi delle righe aggiornate e infine delle righe inserite.

Dim updTable As DataTable = custDS.Tables("Customers")

' First process deletes.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Deleted))

' Next process updates.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent))

' Finally, process inserts.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Added))
[C#]
DataTable updTable = custDS.Tables["Customers"];

// First process deletes.
custDA.Update(updTable.Select(null, null, DataViewRowState.Deleted));

// Next process updates.
custDA.Update(updTable.Select(null, null, DataViewRowState.ModifiedCurrent));

// Finally, process inserts.
custDA.Update(updTable.Select(null, null, DataViewRowState.Added));

Vedere anche

Utilizzo di provider di dati .NET Framework per accedere ai dati | Classe DataSet | Classe DataTable | Classe OleDbDataAdapter | Classe OdbcDataAdapter | Classe SqlDataAdapter