Implementieren von optimistischer Parallelität mit dem SqlDataSource-Steuerelement (VB)
von Scott Mitchell
In diesem Tutorial überprüfen wir die Grundlagen der Steuerung für optimistische Parallelität und untersuchen dann, wie sie mithilfe des SqlDataSource-Steuerelements implementiert wird.
Einführung
Im vorherigen Tutorial haben wir uns mit dem Hinzufügen von Funktionen zum Einfügen, Aktualisieren und Löschen des SqlDataSource-Steuerelements befasst. Kurz gesagt, um diese Features bereitzustellen, mussten wir die entsprechende , oder SQL-Anweisung in den Eigenschaften des Steuerelements InsertCommand
, UpdateCommand
oder zusammen DeleteCommand
mit den entsprechenden Parametern in den InsertParameters
Auflistungen , UpdateParameters
und DeleteParameters
angeben.DELETE
UPDATE
INSERT
Diese Eigenschaften und Auflistungen können zwar manuell angegeben werden, aber die Schaltfläche Erweitert des Datenquellenkonfigurations-Assistenten bietet das Kontrollkästchen Anweisungen generieren INSERT
, UPDATE
und , mit DELETE
dem diese Anweisungen automatisch basierend auf der SELECT
-Anweisung erstellt werden.
Neben dem Kontrollkästchen Anweisungen generierenINSERT
UPDATE
, und DELETE
enthält das Dialogfeld Erweiterte SQL-Generierungsoptionen die Option Optimistische Parallelität verwenden (siehe Abbildung 1). Wenn diese Option aktiviert ist, werden die WHERE
-Klauseln in der automatisch generierten UPDATE
- und DELETE
-Anweisung so geändert, dass sie nur dann aktualisiert oder gelöscht werden, wenn die zugrunde liegenden Datenbankdaten seit dem letzten Laden der Daten in das Raster nicht geändert wurden.
Abbildung 1: Unterstützung für optimistische Parallelität über das Dialogfeld Erweiterte SQL-Generierungsoptionen
Im Tutorial Implementieren optimistischer Parallelität haben wir die Grundlagen der Steuerung für die optimistische Parallelität untersucht und wie sie der ObjectDataSource hinzugefügt wird. In diesem Tutorial retuschieren wir die Grundlagen der Steuerung der optimistischen Parallelität und untersuchen dann, wie sie mithilfe von SqlDataSource implementiert wird.
Eine Zusammenfassung der optimistischen Parallelität
Bei Webanwendungen, die es mehreren gleichzeitigen Benutzern ermöglichen, dieselben Daten zu bearbeiten oder zu löschen, besteht die Möglichkeit, dass ein Benutzer versehentlich änderungen eines anderen überschreibt. Im Tutorial Implementieren optimistischer Parallelität habe ich das folgende Beispiel bereitgestellt:
Stellen Sie sich vor, dass zwei Benutzer, Jisun und Sam, beide eine Seite in einer Anwendung besuchen, die es Besuchern ermöglicht, Produkte über ein GridView-Steuerelement zu aktualisieren und zu löschen. Beide klicken ungefähr zur gleichen Zeit auf die Schaltfläche Bearbeiten für Chai. Jisun ändert den Produktnamen in Chai Tea und klickt auf die Schaltfläche Aktualisieren. Das Nettoergebnis ist eine UPDATE
Anweisung, die an die Datenbank gesendet wird, die alle aktualisierbaren Felder des Produkts festlegt (obwohl Jisun nur ein Feld aktualisiert hat. ProductName
Zu diesem Zeitpunkt enthält die Datenbank die Werte Chai Tea, die Kategorie Getränke, den Lieferanten Exotic Liquids und so weiter für dieses spezielle Produkt. Der Bildschirm "GridView" auf Sam zeigt jedoch weiterhin den Produktnamen in der bearbeitbaren GridView-Zeile als Chai an. Einige Sekunden nach dem Commit von Jisuns Änderungen aktualisiert Sam die Kategorie in Condiments und klickt auf Aktualisieren. Dies führt zu einer UPDATE
Anweisung, die an die Datenbank gesendet wird, die den Produktnamen auf Chai, die CategoryID
auf die entsprechende Condiments-Kategorie-ID usw. festlegt. Jisuns Änderungen am Produktnamen wurden überschrieben.
Abbildung 2 veranschaulicht diese Interaktion.
Abbildung 2: Wenn zwei Benutzer gleichzeitig einen Datensatz aktualisieren, besteht die Möglichkeit, dass ein Benutzer den anderen überschreibt (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Um zu verhindern, dass sich dieses Szenario entwickelt, muss eine Form der Parallelitätssteuerung implementiert werden. Optimistische Parallelität Der Fokus dieses Tutorials basiert auf der Annahme, dass zwar hin und wieder Parallelitätskonflikte auftreten, aber in der überwiegenden Mehrheit der Zeit solche Konflikte nicht auftreten. Wenn also ein Konflikt auftritt, informiert die Optimistische Parallelitätssteuerung den Benutzer einfach darüber, dass seine Änderungen nicht gespeichert werden können, da ein anderer Benutzer die gleichen Daten geändert hat.
Hinweis
Für Anwendungen, bei denen davon ausgegangen wird, dass es viele Parallelitätskonflikte gibt oder solche Konflikte nicht tolerierbar sind, kann stattdessen die pessimistische Parallelitätssteuerung verwendet werden. Eine ausführlichere Erläuterung zur pessimistischen Parallelitätssteuerung finden Sie im Tutorial Implementieren optimistischer Parallelität.
Die Steuerung der optimistischen Parallelität funktioniert, indem sichergestellt wird, dass der datensatz, der aktualisiert oder gelöscht wird, dieselben Werte aufweist wie beim Starten des Aktualisierungs- oder Löschprozesses. Wenn Sie beispielsweise in einem bearbeitbaren GridView auf die Schaltfläche Bearbeiten klicken, werden die Werte des Datensatzes aus der Datenbank gelesen und in TextBoxes und anderen Websteuerelementen angezeigt. Diese ursprünglichen Werte werden von GridView gespeichert. Später, nachdem der Benutzer seine Änderungen vorgenommen und auf die Schaltfläche Aktualisieren klickt, muss die UPDATE
verwendete Anweisung die ursprünglichen Werte sowie die neuen Werte berücksichtigen und den zugrunde liegenden Datenbankdatensatz nur aktualisieren, wenn die ursprünglichen Werte, die der Benutzer bearbeitet hat, mit den Werten identisch sind, die sich noch in der Datenbank befinden. Abbildung 3 zeigt diese Abfolge von Ereignissen.
Abbildung 3: Damit das Update oder löschen erfolgreich ist, müssen die ursprünglichen Werte gleich den aktuellen Datenbankwerten sein (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Es gibt verschiedene Ansätze für die Implementierung optimistischer Parallelität (siehe Peter A. BrombergsOptimistische Parallelitätsaktualisierungslogik für eine kurze Übersicht über eine Reihe von Optionen). Die von SqlDataSource (sowie von den in unserer Datenzugriffsebene verwendeten ADO.NET Typisierten DataSets) wird die WHERE
-Klausel erweitert, um einen Vergleich aller ursprünglichen Werte einzuschließen. Die folgende UPDATE
Anweisung aktualisiert z. B. den Namen und den Preis eines Produkts nur, wenn die aktuellen Datenbankwerte den Werten entsprechen, die beim Aktualisieren des Datensatzes in GridView ursprünglich abgerufen wurden. Die @ProductName
Parameter und @UnitPrice
enthalten die vom Benutzer eingegebenen neuen Werte, während @original_ProductName
und @original_UnitPrice
die Werte enthalten, die beim Klicken auf die Schaltfläche Bearbeiten ursprünglich in gridView geladen wurden:
UPDATE Products SET
ProductName = @ProductName,
UnitPrice = @UnitPrice
WHERE
ProductID = @original_ProductID AND
ProductName = @original_ProductName AND
UnitPrice = @original_UnitPrice
Wie wir in diesem Tutorial sehen werden, ist das Aktivieren der Steuerung für optimistische Parallelität mit SqlDataSource so einfach wie das Aktivieren eines Kontrollkästchens.
Schritt 1: Erstellen einer SqlDataSource, die optimistische Parallelität unterstützt
Öffnen Sie zunächst die OptimisticConcurrency.aspx
Seite aus dem SqlDataSource
Ordner. Ziehen Sie ein SqlDataSource-Steuerelement aus der Toolbox auf die Designer, und legen Sie dessen ID
-Eigenschaft auf festProductsDataSourceWithOptimisticConcurrency
. Klicken Sie als Nächstes im Smarttag des Steuerelements auf den Link Datenquelle konfigurieren. Wählen Sie auf dem ersten Bildschirm des Assistenten aus, um mit dem NORTHWINDConnectionString
zu arbeiten, und klicken Sie auf Weiter.
Abbildung 4: Auswählen von "Arbeiten mit" (Klicken Sie hier, um dasNORTHWINDConnectionString
Bild in voller Größe anzuzeigen)
In diesem Beispiel fügen wir eine GridView hinzu, mit der Benutzer die Products
Tabelle bearbeiten können. Wählen Sie daher auf dem Bildschirm Select-Anweisung konfigurieren die Products
Tabelle aus der Dropdownliste aus, und wählen Sie die ProductID
Spalten , ProductName
, UnitPrice
und Discontinued
aus, wie in Abbildung 5 dargestellt.
Abbildung 5: Geben Sie aus der Products
Tabelle die ProductID
Spalten , ProductName
, UnitPrice
und Discontinued
zurück (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Klicken Sie nach dem Auswählen der Spalten auf die Schaltfläche Erweitert, um das Dialogfeld Erweiterte SQL-Generierungsoptionen zu öffnen. Aktivieren Sie die Kontrollkästchen Anweisungen generierenINSERT
UPDATE
, , und DELETE
verwenden Sie optimistische Parallelität, und klicken Sie auf OK (einen Screenshot finden Sie zurück zu Abbildung 1). Schließen Sie den Assistenten ab, indem Sie auf Weiter und dann auf Fertig stellen klicken.
Nachdem Sie den Assistenten zum Konfigurieren von Datenquellen abgeschlossen haben, nehmen Sie sich einen Moment Zeit, um die resultierenden DeleteCommand
Eigenschaften und UpdateCommand
die Auflistungen und DeleteParameters
UpdateParameters
zu untersuchen. Die einfachste Möglichkeit besteht darin, auf die Registerkarte Quelle in der unteren linken Ecke zu klicken, um die deklarative Syntax der Seite anzuzeigen. Dort finden Sie einen UpdateCommand
Wert von:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
Mit sieben Parametern in der UpdateParameters
Auflistung:
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
runat="server" ...>
<DeleteParameters>
...
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="UnitPrice" Type="Decimal" />
<asp:Parameter Name="Discontinued" Type="Boolean" />
<asp:Parameter Name="original_ProductID" Type="Int32" />
<asp:Parameter Name="original_ProductName" Type="String" />
<asp:Parameter Name="original_UnitPrice" Type="Decimal" />
<asp:Parameter Name="original_Discontinued" Type="Boolean" />
</UpdateParameters>
...
</asp:SqlDataSource>
Auf ähnliche Weise sollten die Eigenschaft und DeleteParameters
die DeleteCommand
Auflistung wie folgt aussehen:
DELETE FROM [Products]
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
runat="server" ...>
<DeleteParameters>
<asp:Parameter Name="original_ProductID" Type="Int32" />
<asp:Parameter Name="original_ProductName" Type="String" />
<asp:Parameter Name="original_UnitPrice" Type="Decimal" />
<asp:Parameter Name="original_Discontinued" Type="Boolean" />
</DeleteParameters>
<UpdateParameters>
...
</UpdateParameters>
...
</asp:SqlDataSource>
Zusätzlich zum Erweitern der WHERE
Klauseln der UpdateCommand
Eigenschaften und DeleteCommand
(und Hinzufügen der zusätzlichen Parameter zu den jeweiligen Parameterauflistungen) werden durch Auswählen der Option Optimistische Parallelität verwenden zwei weitere Eigenschaften angepasst:
- Ändert die
ConflictDetection
-Eigenschaft vonOverwriteChanges
(Standard) inCompareAllValues
- Ändert die
OldValuesParameterFormatString
-Eigenschaft von {0} (Standard) in original_{0} .
Wenn das Datenwebsteuerelement die SqlDataSource-Methode Update()
oder Delete()
die -Methode aufruft, werden die ursprünglichen Werte übergeben. Wenn die SqlDataSource-Eigenschaft ConflictDetection
auf CompareAllValues
festgelegt ist, werden diese ursprünglichen Werte dem Befehl hinzugefügt. Die OldValuesParameterFormatString
-Eigenschaft stellt das Benennungsmuster bereit, das für diese ursprünglichen Wertparameter verwendet wird. Der Assistent zum Konfigurieren von Datenquellen verwendet original_{0} und benennt jeden ursprünglichen Parameter in den UpdateCommand
Eigenschaften und DeleteCommand
und UpdateParameters
und DeleteParameters
Auflistungen entsprechend.
Hinweis
Da wir die Einfügefunktionen des SqlDataSource-Steuerelements nicht verwenden, können Sie die Eigenschaft und ihre InsertCommand
InsertParameters
Auflistung entfernen.
Korrektes Behandeln vonNULL
Werten
Leider funktionieren die vom Assistenten zum Konfigurieren von Datenquellen automatisch generierten erweiterten UPDATE
- und DELETE
-Anweisungen bei Verwendung der optimistischen Parallelität nicht mit Datensätzen, die Werte enthalten NULL
. Um zu sehen, warum, betrachten Sie unsere SqlDataSource-Ressourcen:UpdateCommand
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
[UnitPrice] = @original_UnitPrice AND
[Discontinued] = @original_Discontinued
Die UnitPrice
Spalte in der Products
Tabelle kann Werte enthalten NULL
. Wenn ein bestimmter Datensatz über einen NULL
Wert für UnitPrice
verfügt, wird der WHERE
Klauselteil [UnitPrice] = @original_UnitPrice
immer zu False ausgewertet, da NULL = NULL
immer False zurückgibt. Daher können Datensätze, die Werte enthaltenNULL
, nicht bearbeitet oder gelöscht werden, da die UPDATE
-Anweisungsklauseln WHERE
und DELETE
keine Zeilen zurückgeben, die aktualisiert oder gelöscht werden sollen.
Hinweis
Dieser Fehler wurde erstmals im Juni 2004 in SqlDataSource generiert falsche SQL-Anweisungen an Microsoft gemeldet und soll in der nächsten Version von ASP.NET behoben werden.
Um dies zu beheben, müssen wir die Klauseln in den WHERE
UpdateCommand
Eigenschaften und DeleteCommand
für alle Spalten, die Werte aufweisen NULL
können, manuell aktualisieren. Wechseln Sie [ColumnName] = @original_ColumnName
im Allgemeinen zu:
(
([ColumnName] IS NULL AND @original_ColumnName IS NULL)
OR
([ColumnName] = @original_ColumnName)
)
Diese Änderung kann direkt über das deklarative Markup, über die Optionen UpdateQuery oder DeleteQuery aus dem Eigenschaftenfenster oder über die Registerkarten UPDATE und DELETE in der Option Benutzerdefinierte SQL-Anweisung oder gespeicherte Prozedur angeben im Assistenten Datenquelle konfigurieren vorgenommen werden. Auch hier muss diese Änderung für jede Spalte in der und DeleteCommand
s-Klausel WHERE
vorgenommen werden, die UpdateCommand
Werte enthalten NULL
kann.
Wenn Sie dies auf unser Beispiel anwenden, werden die folgenden geänderten UpdateCommand
- und DeleteCommand
-Werte angezeigt:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
(([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
OR ([UnitPrice] = @original_UnitPrice)) AND
[Discontinued] = @original_Discontinued
DELETE FROM [Products]
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
(([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
OR ([UnitPrice] = @original_UnitPrice)) AND
[Discontinued] = @original_Discontinued
Schritt 2: Hinzufügen eines GridView-Steuerelements mit Bearbeitungs- und Löschoptionen
Nachdem sqlDataSource so konfiguriert ist, dass sie optimistische Parallelität unterstützt, müssen Sie der Seite, die dieses Parallelitätssteuerelement verwendet, nur ein Datenwebsteuerelement hinzufügen. Fügen Sie für dieses Tutorial ein GridView-Objekt hinzu, das sowohl Bearbeitungs- als auch Löschfunktionen bereitstellt. Ziehen Sie hierzu ein GridView-Objekt aus der Toolbox auf die Designer, und legen Sie auf ID
festProducts
. Binden Sie es über das Smarttag von GridView an das ProductsDataSourceWithOptimisticConcurrency
in Schritt 1 hinzugefügte SqlDataSource-Steuerelement. Aktivieren Sie abschließend die Optionen Bearbeiten aktivieren und Löschen aktivieren im Smarttag.
Abbildung 6: Binden von GridView an die SqlDataSource und Aktivieren von Bearbeitung und Löschen (Klicken Sie hier, um das Bild in voller Größe anzuzeigen)
Nachdem Sie gridView hinzugefügt haben, konfigurieren Sie die Darstellung, indem Sie boundField ProductID
entfernen, die ProductName
BoundField-Eigenschaft in HeaderText
Product ändern und die UnitPrice
BoundField-Eigenschaft so aktualisieren, dass die HeaderText
Eigenschaft einfach Price ist. Im Idealfall erweitern wir die Bearbeitungsschnittstelle, um einen RequiredFieldValidator für den ProductName
Wert und einen CompareValidator für den UnitPrice
Wert einzuschließen (um sicherzustellen, dass es sich um einen ordnungsgemäß formatierten numerischen Wert ist). Im Tutorial Anpassen der Datenänderungsschnittstelle finden Sie ausführlichere Informationen zum Anpassen der Bearbeitungsschnittstelle von GridView.
Hinweis
Der GridView-Ansichtsstatus muss aktiviert sein, da die ursprünglichen Werte, die von GridView an die SqlDataSource übergeben werden, im Ansichtszustand gespeichert werden.
Nachdem Sie diese Änderungen am GridView vorgenommen haben, sollte das deklarative Markup GridView und SqlDataSource wie folgt aussehen:
<asp:SqlDataSource ID="ProductsDataSourceWithOptimisticConcurrency"
runat="server" ConflictDetection="CompareAllValues"
ConnectionString="<%$ ConnectionStrings:NORTHWNDConnectionString %>"
DeleteCommand=
"DELETE FROM [Products]
WHERE [ProductID] = @original_ProductID
AND [ProductName] = @original_ProductName
AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
OR ([UnitPrice] = @original_UnitPrice))
AND [Discontinued] = @original_Discontinued"
OldValuesParameterFormatString=
"original_{0}"
SelectCommand=
"SELECT [ProductID], [ProductName], [UnitPrice], [Discontinued]
FROM [Products]"
UpdateCommand=
"UPDATE [Products]
SET [ProductName] = @ProductName, [UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE [ProductID] = @original_ProductID
AND [ProductName] = @original_ProductName
AND (([UnitPrice] IS NULL AND @original_UnitPrice IS NULL)
OR ([UnitPrice] = @original_UnitPrice))
AND [Discontinued] = @original_Discontinued">
<DeleteParameters>
<asp:Parameter Name="original_ProductID" Type="Int32" />
<asp:Parameter Name="original_ProductName" Type="String" />
<asp:Parameter Name="original_UnitPrice" Type="Decimal" />
<asp:Parameter Name="original_Discontinued" Type="Boolean" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="ProductName" Type="String" />
<asp:Parameter Name="UnitPrice" Type="Decimal" />
<asp:Parameter Name="Discontinued" Type="Boolean" />
<asp:Parameter Name="original_ProductID" Type="Int32" />
<asp:Parameter Name="original_ProductName" Type="String" />
<asp:Parameter Name="original_UnitPrice" Type="Decimal" />
<asp:Parameter Name="original_Discontinued" Type="Boolean" />
</UpdateParameters>
</asp:SqlDataSource>
<asp:GridView ID="Products" runat="server"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ProductsDataSourceWithOptimisticConcurrency">
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" HeaderText="Price"
SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
Um das optimistische Parallelitätssteuerelement in Aktion zu sehen, öffnen Sie zwei Browserfenster, und laden Sie die OptimisticConcurrency.aspx
Seite in beide. Klicken Sie in beiden Browsern auf die Schaltflächen Bearbeiten für das erste Produkt. Ändern Sie in einem Browser den Produktnamen, und klicken Sie auf Aktualisieren. Der Browser postback, und gridView kehrt in den Vorbearbeitungsmodus zurück und zeigt den neuen Produktnamen für den gerade bearbeiteten Datensatz an.
Ändern Sie im zweiten Browserfenster den Preis (behalten Sie jedoch den Produktnamen als ursprünglichen Wert bei), und klicken Sie auf Aktualisieren. Beim Postback kehrt das Raster in den Vorbearbeitungsmodus zurück, aber die Änderung des Preises wird nicht aufgezeichnet. Der zweite Browser zeigt den gleichen Wert wie der erste den neuen Produktnamen mit dem alten Preis an. Die im zweiten Browserfenster vorgenommenen Änderungen gingen verloren. Darüber hinaus gingen die Änderungen eher leise verloren, da es keine Ausnahme oder Meldung gab, die darauf hindeutete, dass gerade eine Parallelitätsverletzung aufgetreten ist.
Abbildung 7: Die Änderungen im zweiten Browserfenster wurden unbeaufsichtigt verloren (Klicken Sie hier, um das bild in voller Größe anzuzeigen)
Der Grund, warum die Änderungen des zweiten Browsers nicht übernommen wurden, lag daran, dass die UPDATE
Anweisung s-Klausel WHERE
alle Datensätze herausgefiltert hat und sich daher auf keine Zeilen auswirkte. Sehen wir uns die UPDATE
Anweisung noch einmal an:
UPDATE [Products] SET
[ProductName] = @ProductName,
[UnitPrice] = @UnitPrice,
[Discontinued] = @Discontinued
WHERE
[ProductID] = @original_ProductID AND
[ProductName] = @original_ProductName AND
(([UnitPrice] IS NULL AND @original_UnitPrice IS NULL) OR
([UnitPrice] = @original_UnitPrice)) AND
[Discontinued] = @original_Discontinued
Wenn das zweite Browserfenster den Datensatz aktualisiert, stimmt der in der WHERE
-Klausel angegebene ursprüngliche Produktname nicht mit dem vorhandenen Produktnamen überein (da er vom ersten Browser geändert wurde). Daher gibt die Anweisung [ProductName] = @original_ProductName
False zurück, und die UPDATE
wirkt sich nicht auf Datensätze aus.
Hinweis
Löschen funktioniert auf die gleiche Weise. Wenn zwei Browserfenster geöffnet sind, bearbeiten Sie zunächst ein bestimmtes Produkt mit einem, und speichern Sie dann die Änderungen. Nachdem Sie die Änderungen in dem einen Browser gespeichert haben, klicken Sie auf die Schaltfläche Löschen für dasselbe Produkt im anderen. Da die ursprünglichen Werte in der DELETE
Anweisung s-Klausel WHERE
nicht übereinstimmen, schlägt das löschen im Hintergrund fehl.
Aus sicht des Endbenutzers im zweiten Browserfenster kehrt das Raster nach dem Klicken auf die Schaltfläche Aktualisieren in den Vorbearbeitungsmodus zurück, aber die Änderungen gingen verloren. Es gibt jedoch kein visuelles Feedback, dass ihre Änderungen nicht haften bleiben. Wenn Änderungen eines Benutzers aufgrund einer Parallelitätsverletzung verloren gehen, sollten wir diesen im Idealfall benachrichtigen und das Raster möglicherweise im Bearbeitungsmodus behalten. Sehen wir uns an, wie dies erreicht wird.
Schritt 3: Ermitteln, wann eine Parallelitätsverletzung aufgetreten ist
Da eine Parallelitätsverletzung die vorgenommenen Änderungen ablehnt, wäre es schön, den Benutzer zu benachrichtigen, wenn eine Parallelitätsverletzung aufgetreten ist. Um den Benutzer zu warnen, fügen Sie oben auf der Seite ein Label Web-Steuerelement mit dem Namen ConcurrencyViolationMessage
hinzu, dessen Text
Eigenschaft die folgende Meldung anzeigt: Sie haben versucht, einen Datensatz zu aktualisieren oder zu löschen, der gleichzeitig von einem anderen Benutzer aktualisiert wurde. Überprüfen Sie die Änderungen des anderen Benutzers, und wiederholen Sie dann Ihr Update oder Löschen. Legen Sie die Eigenschaft des Label-Steuerelements CssClass
auf Warning fest. Dies ist eine CSS-Klasse, die in Styles.css
definiert ist, die Text in einer roten, kursiven, fetten und großen Schriftart anzeigt. Legen Sie schließlich die Eigenschaften Bezeichnung s Visible
und EnableViewState
auf fest False
. Dadurch wird die Bezeichnung ausgeblendet, mit Ausnahme der Postbacks, bei denen die Eigenschaft explizit auf True
festgelegt Visible
wird.
Abbildung 8: Hinzufügen eines Beschriftungssteuerelements zur Seite, um die Warnung anzuzeigen (Klicken Sie hier, um das vollständige Bild anzuzeigen)
Beim Ausführen eines Updates oder Löschens werden die GridView-Ereignishandler RowUpdated
und RowDeleted
-Ereignishandler ausgelöst, nachdem die Datenquellensteuerung das angeforderte Update oder Löschen ausgeführt hat. Anhand dieser Ereignishandler können wir bestimmen, wie viele Zeilen von dem Vorgang betroffen waren. Wenn null Zeilen betroffen sind, möchten wir die ConcurrencyViolationMessage
Bezeichnung anzeigen.
Erstellen Sie einen Ereignishandler für die RowUpdated
- und RowDeleted
-Ereignisse, und fügen Sie den folgenden Code hinzu:
Protected Sub Products_RowUpdated(sender As Object, e As GridViewUpdatedEventArgs) _
Handles Products.RowUpdated
If e.AffectedRows = 0 Then
ConcurrencyViolationMessage.Visible = True
e.KeepInEditMode = True
' Rebind the data to the GridView to show the latest changes
Products.DataBind()
End If
End Sub
Protected Sub Products_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
Handles Products.RowDeleted
If e.AffectedRows = 0 Then
ConcurrencyViolationMessage.Visible = True
End If
End Sub
In beiden Ereignishandlern überprüfen wir die e.AffectedRows
-Eigenschaft und legen die Label-Eigenschaft Visible
True
auf festConcurrencyViolationMessage
, wenn sie gleich 0 ist. RowUpdated
Im Ereignishandler weisen wir die GridView außerdem an, im Bearbeitungsmodus zu bleiben, indem wir die KeepInEditMode
Eigenschaft auf true festlegen. Dabei müssen wir die Daten neu an das Raster binden, damit die Daten des anderen Benutzers in die Bearbeitungsschnittstelle geladen werden. Dies wird durch Aufrufen der GridView-Methode DataBind()
erreicht.
Wie Abbildung 9 zeigt, wird bei diesen beiden Ereignishandlern eine sehr auffällige Meldung angezeigt, wenn eine Parallelitätsverletzung auftritt.
Abbildung 9: Eine Meldung wird im Gesicht einer Parallelitätsverletzung angezeigt (Klicken Sie hier, um das bild in voller Größe anzuzeigen)
Zusammenfassung
Beim Erstellen einer Webanwendung, bei der mehrere gleichzeitige Benutzer dieselben Daten bearbeiten, ist es wichtig, Optionen für die Parallelitätssteuerung in Betracht zu ziehen. Standardmäßig verwenden die ASP.NET Datenwebsteuerelemente und Datenquellensteuerelemente kein Parallelitätssteuerelement. Wie wir in diesem Tutorial gesehen haben, ist die Implementierung der optimistischen Parallelitätssteuerung mit SqlDataSource relativ schnell und einfach. SqlDataSource übernimmt die meisten Aufgaben beim Hinzufügen von erweiterten WHERE
Klauseln zu den automatisch generierten UPDATE
Und DELETE
-Anweisungen, aber es gibt einige Feinheiten bei der Behandlung von NULL
Wertspalten, wie im Abschnitt Richtig behandeln von NULL
Werten erläutert.
Dieses Tutorial schließt unsere Untersuchung der SqlDataSource ab. In unseren restlichen Tutorials wird die Arbeit mit Daten mithilfe der ObjectDataSource und der mehrstufigen Architektur wieder hergestellt.
Viel Spaß beim Programmieren!
Zum Autor
Scott Mitchell, Autor von sieben ASP/ASP.NET-Büchern und Gründer von 4GuysFromRolla.com, arbeitet seit 1998 mit Microsoft-Webtechnologien. Scott arbeitet als unabhängiger Berater, Trainer und Autor. Sein neuestes Buch ist Sams Teach Yourself ASP.NET 2.0 in 24 Stunden. Er kann unter mitchell@4GuysFromRolla.comoder über seinen Blog erreicht werden, der unter http://ScottOnWriting.NETzu finden ist.