Mise à jour de la base de données avec un DataAdapter et le DataSet

La méthode Update du DataAdapter est appelée pour répercuter les modifications provenant d'un DataSet dans la source de données. La méthode Update, comme la méthode Fill, prend comme arguments une instance d'un DataSet et un objet DataTable optionnel ou un nom DataTable. L'instance DataSet est le DataSet qui contient les modifications apportées et le DataTable identifie la table où les modifications doivent être extraites.

Lorsque vous appelez la méthode Update, le DataAdapter analyse les modifications apportées et exécute la commande appropriée (INSERT, UPDATE ou DELETE). Lorsque le DataAdapter trouve une modification dans un DataRow, il utilise InsertCommand, UpdateCommand ou DeleteCommand pour traiter la modification. Ceci vous permet d'optimiser la performance de votre application ADO.NET en spécifiant la syntaxe de commande au moment du design et, si possible, par l'intermédiaire de l'utilisation des procédures stockées. Vous devez explicitement définir les commandes avant d'appeler Update. Si Update est appelé et si la commande appropriée n'existe pas pour une mise à jour particulière (par exemple pas de DeleteCommand pour les lignes supprimées), une exception sera levée.

Les paramètres Command peuvent être utilisés pour spécifier les valeurs d'entrée et de sortie pour une instruction SQL ou une procédure stockée pour chaque ligne modifiée dans un DataSet. Pour plus d'informations, consultez Utilisation des paramètres avec un DataAdapter.

Si votre DataTable mappe à une table de base de données unique ou est généré par celle-ci, vous pouvez tirer parti de l'objet CommandBuilder pour générer automatiquement les DeleteCommand, InsertCommand et UpdateCommand du DataAdapter. Pour plus d'informations, consultez Commandes générées automatiquement.

La méthode Update répercutera vos modifications dans la source de données, cependant d'autres clients peuvent avoir modifié les données au niveau de la source depuis la dernière fois où vous avez rempli le DataSet. Pour actualiser votre DataSet avec les données en cours, utilisez le DataAdapter et remplissez (Fill) à nouveau le DataSet. De nouvelles lignes seront ajoutées à la table et les informations mises à jour seront incorporées dans les lignes existantes. La méthode Fill détermine si une nouvelle ligne sera ajoutée ou une ligne existante mise à jour en se fondant sur les valeurs de clé primaire des lignes du DataSet et des lignes retournées par SelectCommand. Si une valeur de clé primaire d'une ligne du DataSet correspond à celle d'une ligne des résultats retournés par SelectCommand, la méthode Fill met à jour la ligne existante en y insérant les informations de la ligne retournée par SelectCommand et définit pour le RowState de la ligne existante la valeur Unchanged. Si la valeur de clé primaire d'une ligne retournée par SelectCommand n'a pas de correspondance dans les lignes du DataSet, la méthode Fill ajoute une nouvelle ligne avec un RowState ayant pour valeur Unchanged.

Remarque   Si SelectCommand retourne les résultats d'un OUTER JOIN, le DataAdapter ne définit par la valeur PrimaryKey du DataTable résultant. Vous devez définir vous-même la propriété PrimaryKey pour garantir une résolution correcte des lignes dupliquées. Pour plus d'informations, consultez Définition d'une clé primaire pour une table.

Pour gérer les exceptions qui peuvent se produire pendant un Update, utilisez l'événement RowUpdated pour répondre aux erreurs de mise à jour des lignes (consultez Utilisation des événements du DataAdapter). Vous pouvez aussi affecter true à DataAdapter.ContinueUpdateOnError avant d'appeler Update et répondre aux informations d'erreur stockées dans la propriété RowError d'une ligne particulière lorsque Update est terminé (consultez Ajout et lecture des informations sur les erreurs de ligne).

**Remarque   **L'appel de AcceptChanges sur les DataSet, DataTable ou DataRow provoquera la substitution de toutes les valeurs Original d'un DataRow par les valeurs Current du DataRow. Si les valeurs de champ qui identifient la ligne comme étant unique ont été modifiées, après avoir appelé AcceptChanges, les valeurs Original ne correspondront plus aux valeurs de la source de données.

Les exemples suivants illustrent l'exécution des mises à jour des lignes modifiées en définissant de manière explicite le UpdateCommand du DataAdapter. Notez que le paramètre spécifié dans la clause WHERE de l'instruction UPDATE est défini pour utiliser la valeur Original de SourceColumn. Ceci est important, car la valeur Current peut avoir été modifiée et ne pas correspondre à la valeur dans la source de données. La valeur Original est la valeur qui a été utilisée pour remplir le DataTable à partir de la source de données.

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);

Colonnes AutoIncrement

Si les tables de votre source de données ont des colonnes qui s'auto-incrémentent, vous pouvez remplir les colonnes de votre DataSet avec les valeurs générées par la source de données, en retournant la valeur d'auto-incrémentation comme paramètre de sortie d'une procédure stockée et en la mappant à une colonne dans une table ou en utilisant l'événement RowUpdated du DataAdapter. Pour en voir un exemple, consultez Extraction des valeurs de champs Identité ou NuméroAuto.

Cependant, les valeurs de votre DataSet peuvent être désynchronisées par rapport aux valeurs de la source de données et résulter en un comportement imprévisible. Prenons par exemple une table avec une colonne de clé primaire auto-incrémentée de CustomerID. Si vous ajoutez deux nouveaux clients dans le DataSet, ils reçoivent les valeurs CustomerId auto-incrémentées de 1 et 2. Lorsque la ligne du deuxième client est passée à la méthode Update du DataAdapter, la ligne nouvellement ajoutée reçoit une valeur CustomerID auto-incrémentée de 1 au niveau de la source de données, qui ne correspond pas à la valeur 2 du DataSet. Lorsque le DataAdapter remplit la ligne dans le DataSet avec la valeur retournée, il y a violation de contrainte, car la ligne du premier client a déjà un CustomerID de 1.

Pour éviter ce comportement, lorsque vous travaillez avec des colonnes auto-incrémentées au niveau d'une source de données et dans un DataSet, il est recommandé de créer la colonne dans le DataSet avec un AutoIncrementStep de -1 et un AutoIncrementSeed de 0 et de vous assurer que votre source de données génère des valeurs d'identité auto-incrémentées commençant par 1 et dont la valeur de pas est positive. En conséquence, le DataSet générera pour les valeurs auto-incrémentées des nombres négatifs qui ne seront pas en conflit avec les valeurs auto-incrémentées positives générées par la source de données. Une autre solution consiste à utiliser les colonnes de type Guid au lieu des colonnes auto-incrémentées. L'algorithme qui génère les valeurs Guid ne doit jamais générer le même Guid dans le DataSet que celui généré par la source de données. Pour plus d'informations sur la définition des colonnes dans un DataTable, consultez Définition du schéma d'un DataTable.

Ordre des insertions, mises à jour et suppressions

Dans de nombreuses circonstances, l'ordre dans lequel les modifications faites dans le DataSet sont transmises à la source de données est important. Par exemple, si une valeur de clé primaire d'une ligne existante est mise à jour et qu'une nouvelle ligne est ajoutée avec la nouvelle valeur de clé primaire, il est important de traiter la mise à jour avant l'insertion.

Vous pouvez utiliser la méthode Select du DataTable pour retourner un tableau DataRow qui fait uniquement référence à des lignes avec un RowState particulier. Vous pouvez alors passer le tableau DataRow retourné à la méthode Update du DataAdapter pour traiter les lignes modifiées. En spécifiant un sous-ensemble des lignes à mettre à jour, vous pouvez contrôler l'ordre dans lequel les insertions, mises à jour et suppressions sont traitées.

Le code suivant garantit, par exemple, que seront d'abord traitées les lignes supprimées de la table puis les lignes mises à jour et enfin les lignes insérées.

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));

Voir aussi

Utilisation des fournisseurs de données .NET Framework pour l'accès aux données | DataSet, classe | DataTable, classe | OleDbDataAdapter, classe | OdbcDataAdapter, classe | SqlDataAdapter, classe