26.4 Änderung einer Datenzeile
26.4.1 Aktualisierungszustand
Ein DataSet ist lokal in der Anwendung abgelegt. Während des Löschens, Änderns und Hinzufügens von Datenzeilen besteht zu der Originaldatenbank keine Verbindung. Wenn der Benutzer die geänderten Daten an die Datenbank übermittelt, muss sich das DataSet daran erinnern, welche Zeilen in welcher Weise von einer Änderung betroffen sind. Haben Sie beispielsweise eine Datenzeile gelöscht, muss für die betreffende Datenzeile ein SQL-DELETE-Kommando zur Datenbank geschickt werden, die das Löschen in der Originaltabelle bewirkt. Haben Sie eine Datenzeile geändert, ist es ein UPDATE-Kommando.
Die Erzeugung der Aktualisierungsabfragen wird im nächsten Abschnitt erklärt. Unabhängig von den Details können Sie bereits erkennen, wie wichtig es ist, dass jede Datenzeile ihren eigenen Aktualisierungszustand kennt.
ADO.NET speichert die unabdingbare Zustandsinformation in der Eigenschaft RowState jeder Datenzeile. Die Eigenschaft wird durch die Enumeration DataRowState beschrieben, deren Werte in Tabelle 26.5 aufgelistet sind.
Konstante | Die Zeile wurde ... |
Added |
einer DataRowCollection hinzugefügt. |
Deleted |
mit der Delete-Methode der DataRow gelöscht. |
Detached |
erstellt, ist jedoch noch nicht oder nicht mehr Teil einer DataRowCollection. |
Modified |
geändert. |
Unchanged |
nicht geändert. |
26.4.2 Ursprünglicher und aktualisierter Inhalt
Eine Datenzeile beschreibt, ob und wie sie modifiziert worden ist. Um die Änderung zur Datenbank zu übermitteln, reicht das aber noch nicht aus. Stellen Sie sich vor, Sie wollen den Artikelbezeichner einer Datenzeile der Products-Tabelle ändern und die Änderung mit einem UPDATE-Kommando der Datenbank mitteilen, zum Beispiel:
UPDATE Products SET ProductName = @Param1 WHERE ProductID = @Param2 AND ProductName = @Param3
Im Parameter @Param1 wird der geänderte, also neue Wert übermittelt, in @Param2 der Schlüsselwert der Datenzeile und in @Param3 der ursprüngliche Wert der Spalte ProductID. Setzen Sie ein solches Kommando ab, darf natürlich zwischen dem Abrufen der Dateninformationen und der Aktualisierung kein zweiter Benutzer den Produktnamen geändert haben. Sonst kommt es zu einer Konfliktsituation, weil die anstehende Änderung nicht in die Datenbank geschrieben werden kann (der andere Benutzer hat die »alten« Werte überschrieben, sodass WHERE ins Leere läuft). Den Details dieser (scheinbaren) Problematik widmen wir uns später.
Auch ohne Einzelheiten ist klar, dass ein UPDATE-Kommando die Originalwerte kennen muss, um die zu ändernden Datensätze zu identifizieren. Deshalb existiert analog zur einfachen Indexierung
row("Productname")
zum Zugriff auf den aktuellen, also lokal geänderten Wert der Spalte ProductName, auch eine Version, die auf den von der Datenbank bezogenen Originalwert zurückgreift:
row("Productname", DataRowVersion.Original)
Die in Tabelle 26.6 gezeigte Aufzählung DataRowVersion spezifiziert die Version der indexierten Daten.
Konstante | Beschreibung |
Current |
Die Zeile enthält aktuelle Werte. |
Default |
Current für die DataRowState-Werte Added, Modified oder Deleted. Proposed für die DataRowState-Wert Detached. |
Original |
Die Zeile enthält ihre ursprünglichen Werte. |
Proposed |
Die Zeile enthält einen vorgeschlagenen Wert. |
Daher hat jede DataRow immer zwei Versionen: unter DataRowVersion.Original die aus der Datenbank bezogenen Werte und unter DataRowVersion.Current die aktuellen und möglicherweise geänderten Werte. Jetzt wird auch verständlich, warum es nach der Einleitung einer Änderung mit BeginEdit mittels CancelEdit möglich ist, den ursprünglichen Zustand einer DataRow zu restaurieren.
Wenn Sie mit
row("ProductName")
den Inhalt einer Spalte abrufen, wird immer DataRowVersion.Current ausgewertet. Das ist wichtig zu wissen, denn sollten Sie die DataRowCollection in einer Schleife durchlaufen, innerhalb der zum Beispiel auf Spalten aller geänderten Zeilen zugegriffen wird, dürfen Sie von einer gelöschten Zeile nicht DataRowVersion.Current abrufen. Sie können aber sehr wohl DataRowVersion.Original auswerten, weil eine als gelöscht markierte Datenzeile nicht aus der DataRowCollection entfernt wird.
Im nächsten Beispiel wird das DataSet aus der Artikeltabelle mit Daten gefüllt und ein weiterer Datensatz hinzugefügt. Anschließend wird in der Tabelle nach einem bestimmten Artikel gesucht (»Tofu«). Hierzu wird die mehrfach überladene Methode Select der DataTable aufgerufen. Benutzt wird in diesem Beispiel die einfach parametrisierte Version, der ein Suchkriterium als Zeichenfolge übergeben wird. Die Zeichenfolge entspricht der WHERE-Klausel in einer SELECT-Abfrage ohne die Angabe von WHERE. Zum Schluss wird auch noch die fünfte Datenzeile aus der Liste »gelöscht«.
An der Konsole werden abschließend nur die lokalen Datenzeilen angezeigt, die in irgend-einer Form eine Änderung gegenüber dem Original erfahren haben.
'...\ADO\DataSet\Zeilenstatus.vb |
Option Strict On Imports System.Data.Common, System.Data.SqlClient Namespace ADO Module Zeilenstatus Sub Test() Dim con As DbConnection = New SqlConnection() con.ConnectionString = "Data Source=(local);" & _ "Initial Catalog=Northwind;Integrated Security=sspi" Dim cmd As DbCommand = New SqlCommand() cmd.Connection = con cmd.CommandText = _ "SELECT ProductID, ProductName, UnitsInStock FROM Products" Dim ds As New DataSet(), da As DbDataAdapter = New SqlDataAdapter() da.SelectCommand = cmd da.Fill(ds) ' Neue Datenzeilen hinzufügen ds.Tables(0).Rows.Add(Nothing, "Camembert", 100) ' Datenzeile ändern Dim editRow As DataRow = ds.Tables(0).Select("ProductName='Tofu'")(0) editRow("UnitsInStock") = 1000 ' Datenzeile löschen ds.Tables(0).Rows(4).Delete() ' Ausgabe der Modifikationen For Each rw As DataRow In ds.Tables(0).Rows : Select Case rw.RowState Case DataRowState.Added Console.WriteLine("Neu: {0}", rw("Productname")) Case DataRowState.Modified Console.WriteLine("Modifiziert: {0} von {1} nach {2}", _ rw("Productname"), rw("UnitsInStock",DataRowVersion.Original), _ rw("UnitsInStock")) Case DataRowState.Deleted Console.WriteLine("Gelöscht: {0}", _ rw("ProductName", DataRowVersion.Original)) End Select : Next Console.ReadLine() End Sub End Module End Namespace
Die Ausgabe zeigt nur die Modifikationen der Tabelle:
Gelöscht: Chef Anton's Gumbo Mix Modifiziert: Tofu von 35 nach 1000 Neu: Camembert
26.4.3 Zeilenstatus manuell steuern
Die Eigenschaft RowState ist für jede Datenzeile nach dem Füllen des DataSets auf Unchanged gesetzt. Je nachdem, ob Sie eine Datenzeile ändern, löschen oder hinzufügen, wird deren Zustand automatisch auf Modified, Deleted oder Added gesetzt.
Mit zwei Methoden können Sie den RowState direkt beeinflussen: AcceptChanges und RejectChanges. Es handelt sich hierbei um Methoden, die Sie auf dem DataSet, der DataTable oder einer bestimmten DataRow aufrufen können.
AcceptChanges
AcceptChanges setzt den RowState einer Datenzeile von Added oder Modified auf Unchanged. Dabei wird der Inhalt von DataRowVersion.Original durch den Inhalt ersetzt, den DataRowVersion.Current beschreibt.
Trifft die Methode auf eine gelöschte Datenzeile, wird die Datenzeile aus der DataRowCollection entfernt und RowState=DataRowState.Detached gesetzt. Rufen Sie AcceptChanges auf die Referenz des DataSets auf, wird mit allen Datenzeilenänderungen in allen Tabellen so verfahren. Der Aufruf auf eine bestimmte Tabelle im DataSet wirkt sich nur auf diese Tabelle aus. Analog können Sie auch den Zustand einer bestimmten Datenzeile ändern.
RejectChanges
Mit RejectChanges setzen Sie alle Änderungen zurück. Die Methode setzt die aktuellen Werte der DataRow auf ihre ursprünglichen Werte zurück. Dabei werden die in der DataRow enthaltenen Änderungen verworfen, also:
DataRowVersion.Current = DataRowVersion.Original
Der RowState hängt nach dem Aufruf von RejectChanges vom anfänglichen RowState ab. Der Zustand Deleted oder Modified wird zu Unchanged, und eine hinzugefügte Datenzeile wird zu Detached.
Die Methoden SetAdded und SetModified
SetAdded ändert den Zustand einer Datenzeile in Added und kann nur für eine DataRow aufgerufen werden, deren RowState den Wert Unchanged oder Added hat. Ist der Ausgangszustand ein anderer, wird die Ausnahme InvalidOperationException ausgelöst.
Dementsprechend ändert SetModified den Zustand in Modified. Der Einsatz dieser Methode beschränkt sich auf Datenzeilen, deren Ausgangszustand Unchanged ist. Ansonsten wird ebenfalls eine Ausnahme ausgelöst.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.