Comme nous l'avons mentionné précédemment, les contrôles liés aux données stockent les valeurs transmises à la source de données dans des dictionnaires indépendants Keys, Values (nouvelles valeurs) et OldValues. Par défaut, SqlDataSource et ObjectDataSource ignorent le champ OldValues et utilisent uniquement des clés et des valeurs. Ce comportement est détecté par la propriété ConflictDetection de la source de données, qui est définie par défaut sur OverwriteChanges. Le modèle OverwriteChanges signifie "faire correspondre uniquement la valeur de la clé primaire afin de mettre à jour ou de supprimer l'enregistrement". Cette opération signifie que la mise à jour ou la suppression d'un enregistrement ne prend pas en compte si la valeur sous-jacente de l'enregistrement a changé. Dans des circonstances normales, l'état idéal est de permettre à l'opération de mise à jour ou de suppression de réussir uniquement lorsque la valeur de la ligne de données correspond exactement à la valeur initialement sélectionnée. Dans cette situation idéale, si un autre utilisateur met à jour une ligne entre le moment où vous la sélectionnez et le moment où vous la mettez à jour, votre opération de mise à jour échouera. La source de données prend également en charge cette opération en définissant la propriété ConflictDetection sur CompareAllValues. Dans ce mode, la source de données appliquera OldValues à la commande ou à la méthode, et elle utilisera ces valeurspour garantir que l'opération de mise à jour ou de suppression doit correspondre à toutes les valeurs de l'enregistrement avant de mettre à jour ou de supprimer le enregistrer. Vous devez également définir la propriété OldValuesParameterFormatString sur une chaîne de format de composant .NET Framework valide (telle que « original_{0} ») pour indiquer comment les paramètres des dictionnaires OldValues et Keys doivent être renommés pour les distinguer des paramètres NewValues.
L'exemple de code suivant montre les commandes SQL typiques utilisées par le contrôle SqlDataSource dans les modes OverwriteChanges et CompareAllValues. Le champ ID est supposé être le champ de clé primaire. Notez que cette dernière commande compare toutes les valeurs brutes des lignes de données dans la clause WHERE, plutôt que uniquement les clés primaires. Dans ce cas, OldValuesParameterFormatString de la source de données doit être défini sur "original_{0}".
SÉLECTIONNEZ [ID], [Nom], [Adresse] dans [Contacts]
-- Remplacer les modifications
MISE À JOUR [Contacts] SET [Nom] = @Nom, [Adresse] = @Adresse OÙ [ID] = @ID
SUPPRIMER DE [Contacts] OÙ [ID] = @ID
-- CompareAllValues
MISE À JOUR [Contacts] SET [Nom] = @Nom, [Adresse] = @Adresse OÙ [ID] = @original_ID
ET [Nom] = @original_Name ET [Adresse] = @original_Address
SUPPRIMER DE [Contacts] OÙ [ID] = @original_ID ET [Nom] = @original_Name
AND [Address] = @original_Address
Veuillez noter que les OldValues ne sont pas requises pour les opérations d'insertion et que ConflictDetection n'a de sens que pour les opérations de mise à jour et de suppression.
L'exemple suivant illustre le comportement en cas de conflit. Pour exécuter cet exemple, vous devez ouvrir deux instances de l'exemple dans deux fenêtres de navigateur distinctes (cliquez deux fois sur "Exécuter l'exemple"). Cliquez ensuite sur le bouton « Modifier » sur la même ligne des deux formulaires pour mettre la ligne en mode édition. Modifiez une valeur dans la première fenêtre et cliquez sur "Mettre à jour". Veuillez noter que la mise à jour a réussi. Dans la deuxième fenêtre, saisissez une nouvelle valeur dans la ligne et cliquez sur "Mettre à jour". Cette opération de mise à jour échoue car la valeur de la ligne de données sous-jacente a déjà été modifiée par la première opération de mise à jour. Cet exemple vérifie la propriété AffectedRows du paramètre d'événement Updated ou Deleted, qui est 0 pour confirmer qu'un conflit s'est produit.
<script runat="serveur">
Sous-protégé SqlDataSource1_Updated (expéditeur en tant qu'objet, et en tant que SqlDataSourceStatusEventArgs)
Si e.AffectedRows = 0 Alors
Response.Write("Ligne modifiée, mise à jour interrompue<br />")
Fin si
Fin du sous-
protégé SqlDataSource1_Deleted (expéditeur en tant qu'objet, et en tant que SqlDataSourceStatusEventArgs)
Si e.AffectedRows = 0 Alors
Response.Write("Ligne modifiée, suppression interrompue<br />")
Fin si
Fin du sous-marin
</script>
Lorsque Update ou Delete utilise l'interface utilisateur basée sur un modèle, les anciennes valeurs des champs de liaison de données bidirectionnelle utilisant la syntaxe Bind seront conservées. Pour Supprimer, cela signifie que vous devez utiliser la syntaxe Bind pour les valeurs liées aux données dans ItemTemplate. Le but est de conserver l'ancienne valeur requise pour l'opération de suppression. L'exemple suivant illustre cette technique.
<Colonnes>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<Modèle d'élément>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("ContactID") %>'></asp:Label>
</Modèle d'élément>
<EditItemTemplate>
<asp:Label ID="Label3" runat="server" Text='<%# Eval("ContactID") %>'></asp:Label>
</EditItemTemplate>
</asp:TemplateField>
<Modèle d'élément>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("ContactName") %>'></asp:Label>
</Modèle d'élément>
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("ContactName") %>'></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
</Colonnes>
</asp:GridView>
Vous pouvez gérer les erreurs de détection de conflit en douceur en invitant l'utilisateur que les données sous-jacentes ont été modifiées, en affichant la valeur modifiée à l'utilisateur et en lui permettant de choisir de soumettre ou d'abandonner son opération. L'exemple suivant montre une manière possible de gérer la détection des conflits. Notez que le paramètre d'événement RowUpdated de DetailsView reçoit un dictionnaire qui peut être utilisé pour détecter les valeurs saisies par l'utilisateur. Vous pouvez également définir la propriété KeepInEditMode de ce paramètre d'événement pour conserver DetailsView en mode édition pendant que l'utilisateur décide comment gérer le conflit. Cet exemple teste une approche similaire au précédent, ouvrant deux fenêtres simultanément pour créer des mises à jour conflictuelles.
Sous-protégé DetailsView1_ItemUpdated (expéditeur ByVal en tant qu'objet, ByVal et en tant que System.Web.UI.WebControls.DetailsViewUpdatedEventArgs)
Si e.AffectedRows = 0 Alors
' Mettez DetailsView en mode édition et synchronisez avec la base de données e.KeepInEditMode = True
DétailsView1.DataBind()
' Remplir le DetailsView avec les valeurs saisies par l'utilisateur
Dim t comme zone de texte
t = DétailsView1.Rows(1).Cells(1).Controls(0)
t.Text = e.NewValues("OrderDate")
t = DétailsView1.Rows(2).Cells(1).Controls(0)
t.Text = e.NewValues("ShipCountry")
ErrorPanel.Visible = True
Autre
ErrorPanel.Visible = Faux
Fin si
Fin du sous-marin
protégé DetailsView1_ModeChanging (expéditeur ByVal en tant qu'objet, ByVal et en tant que System.Web.UI.WebControls.DetailsViewModeEventArgs)
Si e.CancelingEdit = True AndAlso ErrorPanel.Visible = True Alors
ErrorPanel.Visible = Faux
Fin si
La situation est similaire lorsque
End Sub
utilise ObjectDataSource.Notez que, étant donné que la propriété ConflictDetection de la source de données est définie sur CompareAllValues, la source de données recherchera une surcharge UpdateContact qui accepte la valeur brute de chaque champ de l'objet Contact.
Vous pouvez également utiliser la propriété DataObjectTypeName et CompareAllValues ensemble. Dans ce cas, ObjectDataSource recherche une surcharge UpdateContact qui n'accepte que deux paramètres (tous deux Contact). Le premier paramètre est l'objet Contact qui stocke la nouvelle valeur et le deuxième paramètre est l'objet Contact qui stocke l'ancienne valeur.