Commandes générées automatiquement

Dans les situations où SelectCommand est spécifié de manière dynamique au moment de l'exécution, par l'intermédiaire d'un outil de requête qui prend une commande textuelle provenant de l'utilisateur par exemple, il est possible que vous ne puissiez pas spécifier les InsertCommand, UpdateCommand ou DeleteCommand appropriés au moment du design. Si votre DataTable mappe à une table de base de données unique ou est généré par elle, vous pouvez tirer parti de l'objet CommandBuilder pour générer automatiquement les DeleteCommand, InsertCommand et UpdateCommand du DataAdapter.

Vous devez au minimum définir la propriété SelectCommand afin que la génération automatique de commandes fonctionne. Le schéma de table extrait par SelectCommand détermine la syntaxe des instructions INSERT, UPDATE et DELETE générées automatiquement.

CommandBuilder doit exécuter SelectCommand afin de retourner les métadonnées nécessaires à la construction des commandes d'insertion, de mise à jour et de suppression. En conséquence, un trajet supplémentaire vers la source de données est nécessaire et peut gêner la performance. Pour parvenir à une performance optimale, spécifiez vos commandes explicitement plutôt que d'utiliser CommandBuilder.

SelectCommand doit aussi retourner au moins une clé primaire ou une colonne unique. Si aucune n'est présente, une exception InvalidOperation est levée et les commandes ne sont pas générées.

Lorsqu'il est associé à DataAdapter, CommandBuilder en génère automatiquement les propriétés InsertCommand, UpdateCommand et DeleteCommand si ce sont des références null. Si Command existe déjà pour une propriété, elle est utilisée.

Les vues de base de données qui sont créées en reliant deux ou plusieurs tables ne sont pas considérées comme une table de base de données unique. Dans cette instance, vous ne pourrez pas utiliser CommandBuilder pour générer automatiquement les commandes ; vous devrez les spécifier de manière explicite. Pour plus d'informations sur la définition explicite des commandes pour répercuter les mises à jour d'un DataSet dans la source de données, consultez Mise à jour de la base de données avec un DataAdapter et le DataSet.

Il est possible de mapper les paramètres de sortie à la ligne mise à jour d'un DataSet. Une tâche courante consisterait à extraire la valeur d'un champ Identité généré automatiquement ou d'un horodatage provenant de la source de données. CommandBuilder ne mappera pas les paramètres de sortie aux colonnes dans une ligne mise à jour par défaut. Dans cette instance, vous devrez explicitement spécifier votre commande. Pour obtenir un exemple de mappage d'un champ Identité généré automatiquement à une colonne dans une ligne insérée, consultez Extraction des valeurs de champs Identité ou NuméroAuto.

Règles pour les commandes générées automatiquement

Le tableau suivant présente les règles de génération des commandes générées automatiquement.

Commande Règle
InsertCommand Insère une ligne dans la source de données pour toutes les lignes de la table dont le RowState a la valeur DataRowState.Added. Insère des valeurs pour toutes les colonnes qui peuvent être mises à jour (mais pas les colonnes comme les identités, les expressions ou les horodatages).
UpdateCommand Met à jour dans la source de données toutes les lignes de la table dont le RowState a la valeur DataRowState.Modified. Met à jour les valeurs de toutes les colonnes à l'exception de celles qui ne peuvent pas être mises à jour (identités ou expressions, par exemple). Met à jour toutes les lignes où les valeurs de colonne au niveau de la source de données correspondent aux valeurs de colonne de clé primaire de la ligne et où les colonnes restantes correspondent aux valeurs d'origine de la ligne. Pour plus d'informations, consultez la section de cette rubrique concernant le Modèle d'accès concurrentiel optimiste pour les mises à jour et les suppressions.
DeleteCommand Supprime dans la source de données toutes les lignes de la table dont le RowState a la valeur DataRowState.Deleted. Supprime toutes les lignes lorsque les valeurs de colonne correspondent aux valeurs de colonne de clé primaire de la ligne et lorsque les colonnes restantes de la source de données correspondent aux valeurs originales de la ligne. Pour plus d'informations, consultez la section de cette rubrique concernant le Modèle d'accès concurrentiel optimiste pour les mises à jour et les suppressions.

Modèle d'accès concurrentiel optimiste pour les mises à jour et les suppressions

La logique de génération automatique de commandes pour les instructions UPDATE et DELETE est fondée sur l'accès concurrentiel optimiste. C'est-à-dire que l'édition des enregistrements n'est pas verrouillée et ils peuvent être modifiés par d'autres utilisateurs ou processus à tout moment. Puisqu'un enregistrement aurait pu être modifié après avoir été retourné de l'instruction SELECT mais avant que l'instruction UPDATE ou DELETE ne soit émise, l'instruction UPDATE ou DELETE générée automatiquement contient une clause WHERE de sorte qu'une ligne n'est mise à jour que si elle contient toutes les valeurs d'origine et n'a pas été supprimée de la source de données. Ceci permet d'éviter que de nouvelles données ne soient remplacées. Au cas où une mise à jour générée automatiquement tente de mettre à jour une ligne qui a été supprimée ou qui ne contient pas les valeurs d'origine qui se trouvent dans le DataSet, la commande n'affectera pas d'enregistrements et DBConcurrencyException sera levé.

Si vous souhaitez que UPDATE ou DELETE soit effectué indépendamment des valeurs d'origine, vous devez explicitement définir UpdateCommand pour le DataAdapter et ne pas compter sur la génération automatique de commandes.

Limites de la logique de génération automatique de commandes

Les limites suivantes s'appliquent à la génération automatique de commandes.

Tables dissociées uniquement

La logique de génération automatique de commandes génère les instructions INSERT, UPDATE ou DELETE pour les tables autonomes sans prendre en compte les relations avec les autres tables au niveau de la source de données. En conséquence, il est possible que l'appel à Update, pour soumettre les modifications pour une colonne qui participe à une contrainte de clé externe dans la base de données, échoue. Pour éviter cette exception, n'utilisez pas CommandBuilder pour mettre à jour les colonnes impliquées dans une contrainte de clé externe et spécifiez plutôt explicitement les instructions utilisées pour effectuer l'opération.

Noms de table et de colonne

La logique de génération automatique de commandes échoue si les noms de colonne ou de table contiennent des caractères spéciaux (notamment espaces, points, points d'interrogation ou autres caractères non alphanumériques), même s'ils sont délimités par des crochets. Les noms de table qualifiés complets sous la forme catalog.schema.table sont pris en charge.

Utilisation de CommandBuilder pour générer automatiquement une instruction SQL

Pour générer automatiquement des instructions SQL pour un DataAdapter, définissez-en d'abord la propriété SelectCommand. Créez ensuite un objet CommandBuilder et spécifiez comme argument le DataAdapter pour lequel CommandBuilder générera automatiquement les instructions SQL.

Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", nwindConn)
Dim custCB As SqlCommandBuilder = New SqlCommandBuilder(custDA)
custCB.QuotePrefix = "["
custCB.QuoteSuffix = "]"

Dim custDS As DataSet = New DataSet

nwindConn.Open()
custDA.Fill(custDS, "Customers")

' Code to modify data in the DataSet here.

' Without the SqlCommandBuilder, this line would fail.
custDA.Update(custDS, "Customers")
nwindConn.Close()
[C#]
SqlDataAdapter custDA = new SqlDataAdapter("SELECT * FROM Customers", nwindConn);
SqlCommandBuilder custCB = new SqlCommandBuilder(custDA);
custCB.QuotePrefix = "[";
custCB.QuoteSuffix = "]";

DataSet custDS = new DataSet();

nwindConn.Open();
custDA.Fill(custDS, "Customers");

// Code to modify data in the DataSet here.

// Without the SqlCommandBuilder, this line would fail.
custDA.Update(custDS, "Customers");
nwindConn.Close();

Modification de SelectCommand

Si vous modifiez le CommandText de SelectCommand après la génération automatique des commandes d'insertion, de mise à jour ou de suppression, une exception peut se produire. Si le SelectCommand.CommandText modifié contient des informations de schéma qui ne sont pas cohérentes par rapport au SelectCommand.CommandText utilisé lors de la génération automatique des commandes d'insertion, de mise à jour ou de suppression, les appels ultérieurs à la méthode DataAdapter.Update peuvent chercher à accéder à des colonnes qui n'existent plus dans la table actuelle référencée par SelectCommand et une exception sera levée.

Vous pouvez actualiser les informations de schéma utilisées par CommandBuilder pour générer automatiquement les commandes en appelant la méthode RefreshSchema du CommandBuilder.

Si vous souhaitez connaître les commandes générées automatiquement, vous pouvez en obtenir une référence avec les méthodes GetInsertCommand, GetUpdateCommand et GetDeleteCommand de l'objet CommandBuilder et vérifier la propriété CommandText du Command associé.

L'exemple de code suivant écrit sur la console la commande de mise à jour générée automatiquement.

Console.WriteLine(custCB.GetUpdateCommand().CommandText)

L'exemple suivant continue le code de l'exemple précédent (section Utilisation de CommandBuilder pour générer automatiquement une instruction SQL) et recrée la table Customers, en remplaçant la colonne CompanyName par ContactName. La méthode RefreshSchema est appelée pour actualiser les commandes générées automatiquement avec ces nouvelles informations de colonne.

nwindConn.Open()

custDA.SelectCommand.CommandText = "SELECT CustomerID, ContactName FROM Customers"
custCB.RefreshSchema()

custDS.Tables.Remove(custDS.Tables("Customers"))
custDA.Fill(custDS, "Customers")

' Code to modify the new table in the DataSet here.

' Without the call to RefreshSchema, this line would fail.
custDA.Update(custDS, "Customers")

nwindConn.Close()
[C#]
nwindConn.Open();

custDA.SelectCommand.CommandText = "SELECT CustomerID, ContactName FROM Customers";
custCB.RefreshSchema();

custDS.Tables.Remove(custDS.Tables["Customers"]);
custDA.Fill(custDS, "Customers");

// Code to modify the new table in the DataSet here.

// Without the call to RefreshSchema, this line would fail.
custDA.Update(custDS, "Customers");

nwindConn.Close();

Voir aussi

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