Wie bereits erwähnt, speichern datengebundene Steuerelemente die an die Datenquelle übergebenen Werte in unabhängigen Wörterbüchern für Schlüssel, Werte (neue Werte) und OldValues. Standardmäßig ignorieren SqlDataSource und ObjectDataSource das OldValues-Feld und verwenden nur Schlüssel und Werte. Dieses Verhalten wird von der ConflictDetection-Eigenschaft der Datenquelle erkannt, die standardmäßig auf OverwriteChanges festgelegt ist. Das OverwriteChanges-Muster bedeutet „nur den Primärschlüsselwert abgleichen, um den Datensatz zu aktualisieren oder zu löschen“. Dieser Vorgang bedeutet, dass beim Aktualisieren oder Löschen eines Datensatzes nicht berücksichtigt wird, ob sich der zugrunde liegende Wert des Datensatzes geändert hat. Unter normalen Umständen besteht der ideale Zustand darin, den Aktualisierungs- oder Löschvorgang nur dann erfolgreich durchzuführen, wenn der Wert der Datenzeile genau mit dem ursprünglich ausgewählten Wert übereinstimmt. Wenn in dieser idealen Situation ein anderer Benutzer eine Zeile zwischen dem Zeitpunkt Ihrer Auswahl und dem Zeitpunkt Ihrer Aktualisierung aktualisiert, schlägt Ihr Aktualisierungsvorgang fehl. Die Datenquelle unterstützt diesen Vorgang auch, indem sie die ConflictDetection-Eigenschaft auf CompareAllValues festlegt. In diesem Modus wendet die Datenquelle OldValues auf den Befehl oder die Methode an und verwendet diese Werte, um sicherzustellen, dass der Aktualisierungs- oder Löschvorgang mit allen Werten des Datensatzes übereinstimmen muss, bevor er aktualisiert oder gelöscht wird aufzeichnen. Sie müssen außerdem die OldValuesParameterFormatString-Eigenschaft auf eine gültige .NET Framework-Komponentenformatzeichenfolge (z. B. „original_{0}“) festlegen, um anzugeben, wie Parameter in den OldValues- und Keys-Wörterbüchern umbenannt werden sollen, um sie von NewValues-Parametern zu unterscheiden.
Das folgende Codebeispiel zeigt typische SQL-Befehle, die vom SqlDataSource-Steuerelement in den Modi OverwriteChanges und CompareAllValues verwendet werden. Es wird davon ausgegangen, dass das ID-Feld das Primärschlüsselfeld ist. Beachten Sie, dass der letztere Befehl alle Rohwerte der Datenzeilen in der WHERE-Klausel vergleicht und nicht nur die Primärschlüssel. In diesem Fall muss der OldValuesParameterFormatString der Datenquelle auf „original_{0}“ gesetzt werden.
WÄHLEN Sie [ID], [Name], [Adresse] aus [Kontakte]
-- Änderungen überschreiben
UPDATE [Kontakte] SET [Name] = @Name, [Adresse] = @Adresse WHERE [ID] = @ID
DELETE FROM [Contacts] WHERE [ID] = @ID
– CompareAllValues
UPDATE [Kontakte] SET [Name] = @Name, [Adresse] = @Adresse WHERE [ID] = @original_ID
AND [Name] = @original_Name AND [Address] = @original_Address
LÖSCHEN AUS [Kontakte] WHERE [ID] = @original_ID AND [Name] = @original_Name
AND [Address] = @original_Address
Bitte beachten Sie, dass OldValues für Einfügevorgänge nicht erforderlich sind und ConflictDetection nur für Aktualisierungs- und Löschvorgänge von Bedeutung ist.
Das folgende Beispiel demonstriert das Verhalten bei Auftreten eines Konflikts. Um dieses Beispiel auszuführen, müssen Sie zwei Instanzen des Beispiels in zwei separaten Browserfenstern öffnen (klicken Sie zweimal auf „Beispiel ausführen“). Klicken Sie dann in derselben Zeile beider Formulare auf die Schaltfläche „Bearbeiten“, um die Zeile in den Bearbeitungsmodus zu versetzen. Ändern Sie im ersten Fenster einen Wert und klicken Sie auf „Aktualisieren“. Bitte beachten Sie, dass das Update erfolgreich war. Geben Sie im zweiten Fenster einen neuen Wert in die Zeile ein und klicken Sie auf „Aktualisieren“. Dieser Aktualisierungsvorgang ist nicht erfolgreich, da der Wert der zugrunde liegenden Datenzeile bereits durch den ersten Aktualisierungsvorgang geändert wurde. In diesem Beispiel wird die AffectedRows-Eigenschaft des Ereignisparameters „Updated“ oder „Deleted“ überprüft, die 0 ist, um zu bestätigen, dass ein Konflikt aufgetreten ist.
<script runat="server">
Protected Sub SqlDataSource1_Updated(sender als Objekt, e als SqlDataSourceStatusEventArgs)
Wenn e.AffectedRows = 0, dann
Response.Write("Zeile geändert, Aktualisierung abgebrochen<br />")
Ende wenn
End Sub
Protected Sub SqlDataSource1_Deleted(sender As Object, e As SqlDataSourceStatusEventArgs)
Wenn e.AffectedRows = 0, dann
Response.Write("Zeile geändert, Löschen abgebrochen<br />")
Ende wenn
Sub beenden
</script>
Wenn „Aktualisieren“ oder „Löschen“ die Benutzeroberfläche mit Vorlagen verwendet, werden die alten Werte der bidirektionalen Datenbindungsfelder mithilfe der Bind-Syntax beibehalten. Für das Löschen bedeutet dies, dass Sie die Bind-Syntax für datengebundene Werte in der ItemTemplate verwenden müssen. Der Zweck besteht darin, den für den Löschvorgang erforderlichen alten Wert beizubehalten. Das folgende Beispiel demonstriert diese Technik.
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:Label ID="Label1" runat="server" Text='<%# Bind("ContactID") %>'></asp:Label>
</ItemTemplate>
<asp:Label ID="Label3" runat="server" Text='<%# Eval("ContactID") %>'></asp:Label>
</EditItemTemplate>
</asp:TemplateField>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("ContactName") %>'></asp:Label>
</ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("ContactName") %>'></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
</Spalten>
</asp:GridView>
Sie können Konflikterkennungsfehler sanft behandeln, indem Sie den Benutzer auffordern, dass die zugrunde liegenden Daten geändert wurden, dem Benutzer den geänderten Wert anzeigen und dem Benutzer die Möglichkeit geben, den Vorgang zu senden oder abzubrechen. Das folgende Beispiel zeigt eine mögliche Vorgehensweise zur Konflikterkennung. Beachten Sie, dass dem RowUpdated-Ereignisparameter von DetailsView ein Wörterbuch übergeben wird, das zum Erkennen vom Benutzer eingegebener Werte verwendet werden kann. Sie können auch die KeepInEditMode-Eigenschaft dieses Ereignisparameters festlegen, um die DetailsView im Bearbeitungsmodus zu halten, während der Benutzer entscheidet, wie mit dem Konflikt umgegangen werden soll. In diesem Beispiel wird ein ähnlicher Ansatz wie im vorherigen getestet, bei dem zwei Fenster gleichzeitig geöffnet werden, um widersprüchliche Updates zu erstellen.
Protected Sub DetailsView1_ItemUpdated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewUpdatedEventArgs)
Wenn e.AffectedRows = 0, dann
' DetailsView in den Bearbeitungsmodus versetzen und mit der Datenbank synchronisieren e.KeepInEditMode = True
DetailsView1.DataBind()
' Füllen Sie die DetailsView erneut mit vom Benutzer eingegebenen Werten
Dimmen Sie es als TextBox
t = DetailsView1.Rows(1).Cells(1).Controls(0)
t.Text = e.NewValues("OrderDate")
t = DetailsView1.Rows(2).Cells(1).Controls(0)
t.Text = e.NewValues("ShipCountry")
ErrorPanel.Visible = True
Anders
ErrorPanel.Visible = False
Ende wenn
End Sub
Protected Sub DetailsView1_ModeChanging(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DetailsViewModeEventArgs)
Wenn e.CancelingEdit = True AndAlso ErrorPanel.Visible = True Then
ErrorPanel.Visible = False
Ende wenn
Die Situation ist ähnlich, wenn
End Sub
ObjectDataSource verwendet.Beachten Sie, dass die Datenquelle nach einer UpdateContact-Überladung sucht, die den Rohwert jedes Felds des Kontaktobjekts akzeptiert, da die ConflictDetection-Eigenschaft der Datenquelle auf CompareAllValues festgelegt ist.
Sie können die DataObjectTypeName-Eigenschaft und CompareAllValues auch zusammen verwenden. In diesem Fall sucht die ObjectDataSource nach einer UpdateContact-Überladung, die nur zwei Parameter (beide Contact) akzeptiert. Der erste Parameter ist das Kontaktobjekt, das den neuen Wert speichert, und der zweite Parameter ist das Kontaktobjekt, das den alten Wert speichert.