Aktualisieren der Datenbank mit einem DataAdapter und dem DataSet
Die Update-Methode des DataAdapter-Objekts wird aufgerufen, um Änderungen aus einem DataSet in die Datenquelle zu übernehmen. Als Argumente verwendet die Update-Methode genau wie die Fill-Methode eine Instanz eines DataSets und ein optionales DataTable-Objekt oder einen DataTable-Namen. Die DataSet-Instanz ist das DataSet, das die vorgenommenen Änderungen enthält. Mit der DataTable wird die Tabelle identifiziert, aus der die Änderungen abgerufen werden.
Wenn Sie die Update-Methode aufrufen, analysiert das DataAdapter-Objekt die vorgenommenen Änderungen und führt den entsprechenden Befehl (INSERT, UPDATE oder DELETE) aus. Wenn das DataAdapter-Objekt eine Änderung an einer DataRow feststellt, wird die Änderung mit InsertCommand, UpdateCommand oder DeleteCommand verarbeitet. Dadurch haben Sie die Möglichkeit, die Leistung der ADO.NET-Anwendung zu maximieren, indem Sie die Befehlssyntax zur Entwurfszeit und, soweit möglich, durch gespeicherte Prozeduren angeben. Sie müssen die Befehle explizit festlegen, bevor Sie die Update-Methode aufrufen. Wenn die Update-Methode aufgerufen wird und der entsprechende Befehl für eine bestimmte Aktualisierung nicht vorhanden ist (z. B. kein DeleteCommand für gelöschte Zeilen), wird eine Ausnahme ausgelöst.
Command-Parameter können verwendet werden, um Eingabe- und Ausgabewerte für eine SQL-Anweisung oder gespeicherte Prozedur für jede bearbeitete Zeile in einem DataSet anzugeben. Weitere Informationen finden Sie unter Verwenden von Parametern mit einem DataAdapter.
Wenn die DataTable einer einzelnen Datenbanktabelle zugeordnet ist oder daraus generiert wurde, können Sie das CommandBuilder-Objekt verwenden, um automatisch DeleteCommand, InsertCommand und UpdateCommand für das DataAdapter-Objekt zu generieren. Weitere Informationen finden Sie unter Automatisch generierte Befehle.
Mit der Update-Methode werden die Änderungen zurück in die Datenquelle übernommen, es kann jedoch sein, dass andere Clients Daten in der Datenquelle geändert haben, seit Sie das DataSet zuletzt gefüllt haben. Zum Aktualisieren des DataSets mit aktuellen Daten verwenden Sie das DataAdapter-Objekt und füllen das DataSet erneut mit der Fill-Methode. Der Tabelle werden neue Zeilen hinzugefügt, und aktualisierte Informationen werden in die vorhandenen Zeilen eingefügt. Durch eine Untersuchung der Primärschlüsselwerte der Zeilen im DataSet und der von SelectCommand zurückgegebenen Zeilen wird mit der Fill-Methode festgelegt, ob eine neue Zeile hinzugefügt oder eine vorhandene Zeile aktualisiert wird. Trifft die Fill-Methode auf einen Primärschlüsselwert für eine Zeile im DataSet, der mit dem Primärschlüsselwert einer Zeile aus den von SelectCommand zurückgegebenen Ergebnissen übereinstimmt, aktualisiert sie die vorhandene Zeile mit den Informationen aus der von SelectCommand zurückgegebenen Zeile und legt für den RowState der vorhandenen Zeile Unchanged fest. Wenn der Primärschlüsselwert einer von SelectCommand zurückgegebenen Zeile keinem der Primärschlüsselwerte der Zeilen im DataSet entspricht, fügt die Fill-Methode eine neue Zeile mit Unchanged als RowState hinzu.
Hinweis Wenn SelectCommand die Ergebnisse eines OUTER JOIN zurückgibt, legt der DataAdapter keinen PrimaryKey-Wert für die sich daraus ergebende DataTable fest. Sie müssen den PrimaryKey selbst definieren, um sicherzustellen, dass doppelte Reihen ordnungsgemäß aufgelöst werden. Weitere Informationen hierzu finden Sie unter Definieren eines Primärschlüssels für eine Tabelle.
Zur Behandlung von Ausnahmen, die während eines Update-Vorgangs auftreten können, können Sie das RowUpdated-Ereignis verwenden, um auf Fehler beim Aktualisieren von Zeilen zu reagieren (siehe Arbeiten mit DataAdapter-Ereignissen). Sie können aber auch für DataAdapter.ContinueUpdateOnError den Wert True festlegen, bevor Sie die Update-Methode aufrufen, und auf die in der RowError-Eigenschaft einer bestimmten Zeile gespeicherten Fehlerinformationen reagieren, wenn der Update-Vorgang abgeschlossen ist (siehe Hinzufügen und Lesen von Informationen zu Zeilenfehlern).
**Hinweis **Wenn Sie AcceptChanges für das DataSet, die DataTable oder die DataRow aufrufen, werden alle Original-Werte einer DataRow mit den Current-Werten der DataRow überschrieben. Wenn die Feldwerte, die eine Zeile als eindeutig kennzeichnen, geändert wurden, entsprechen die Werte in der Datenquelle nach dem Aufrufen von AcceptChanges nicht mehr den Original-Werten.
Die folgenden Beispiele veranschaulichen, wie Aktualisierungen an geänderten Zeilen vorgenommen werden, indem der UpdateCommand des DataAdapter-Objekts explizit festgelegt wird. Beachten Sie, dass für den in der WHERE-Klausel der UPDATE-Anweisung festgelegten Parameter der Original-Wert der SourceColumn verwendet wird. Dies ist wichtig, weil der Current-Wert u. U. geändert wurde und möglicherweise nicht dem Wert in der Datenquelle entspricht. Der Original-Wert ist der Wert, mit dem die DataTable aus der Datenquelle gefüllt wird.
SqlClient
Dim catDA As SqlDataAdapter = New SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)
catDA.UpdateCommand = New SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " & _
"WHERE CategoryID = @CategoryID", nwindConn)
catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")
Dim workParm As SqlParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original
Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")
Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"
catDA.Update(catDS)
[C#]
SqlDataAdapter catDA = new SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);
catDA.UpdateCommand = new SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " +
"WHERE CategoryID = @CategoryID" , nwindConn);
catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");
SqlParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;
DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");
DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
catDA.Update(catDS);
OleDb
Dim catDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)
catDA.UpdateCommand = New OleDbCommand("UPDATE Categories SET CategoryName = ? " & _
"WHERE CategoryID = ?" , nwindConn)
catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName")
Dim workParm As OleDbParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original
Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")
Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"
catDA.Update(catDS)
[C#]
OleDbDataAdapter catDA = new OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);
catDA.UpdateCommand = new OleDbCommand("UPDATE Categories SET CategoryName = ? " +
"WHERE CategoryID = ?" , nwindConn);
catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName");
OleDbParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;
DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");
DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
catDA.Update(catDS);
Odbc
Dim catDA As OdbcDataAdapter = New OdbcDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)
catDA.UpdateCommand = New OdbcCommand("UPDATE Categories SET CategoryName = ? " & _
"WHERE CategoryID = ?" , nwindConn)
catDA.UpdateCommand.Parameters.Add("@CategoryName", OdbcType.VarChar, 15, "CategoryName")
Dim workParm As OdbcParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OdbcType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original
Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")
Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"
Dim modRows() As DataRow = catDS.Tables("Categories").Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent)
catDA.Update(modRows)
[C#]
OdbcDataAdapter catDA = new OdbcDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);
catDA.UpdateCommand = new OdbcCommand("UPDATE Categories SET CategoryName = ? " +
"WHERE CategoryID = ?" , nwindConn);
catDA.UpdateCommand.Parameters.Add("@CategoryName", OdbcType.VarChar, 15, "CategoryName");
OdbcParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OdbcType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;
DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");
DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
DataRow[] modRows = catDS.Tables["Categories"].Select(null, null, DataViewRowState.ModifiedCurrent);
catDA.Update(modRows);
AutoIncrement-Spalten
Wenn die Tabellen aus der Datenquelle Spalten mit automatischer Erhöhung besitzen, können Sie die Spalten im DataSet mit den Werten füllen, die von der Datenquelle generiert wurden, indem Sie die sich automatisch erhöhenden Werte als Ausgabeparameter einer gespeicherten Prozedur zurückgeben und diesen einer Spalte in einer Tabelle zuordnen oder indem Sie das RowUpdated-Ereignis des DataAdapter-Objekts verwenden. Ein Beispiel hierfür finden Sie unter Abrufen von Identitäts- oder AutoWert-Werten.
Es kann jedoch passieren, dass die Werte im DataSet nicht mehr mit den Werten in der Datenquelle übereinstimmen, was zu einem unerwarteten Verhalten führen kann. Stellen Sie sich eine Tabelle mit einer Primärschlüsselspalte CustomerID
vor, die sich automatisch erhöht. Wenn Sie zwei neue Kunden im DataSet einfügen, werden ihnen die sich automatisch erhöhenden CustomerId
-Werte 1
und 2
zugewiesen. Wenn die zweite Kundenzeile an die Update-Methode des DataAdapter-Objekts übergeben wird, erhält die neu hinzugefügte Zeile einen sich automatisch erhöhenden CustomerID
-Wert von 1
in der Datenquelle, der nicht mit dem Wert 2
im DataSet übereinstimmt. Wenn das DataAdapter-Objekt die Zeile im DataSet mit dem Rückgabewert füllt, tritt eine Einschränkungsverletzung auf, weil die erste Kundenzeile bereits die CustomerID
1
besitzt.
Dieses Verhalten kann allerdings vermieden werden. Wenn Sie sowohl in der Datenquelle als auch im DataSet mit Spalten arbeiten, die sich automatisch erhöhen, erstellen Sie die Spalte im DataSet mit **AutoIncrementStep **-1 und AutoIncrementSeed 0. Darüber hinaus müssen Sie sicherstellen, dass die Datenquelle sich automatisch erhöhende Identitätswerte erstellt, die bei 1 beginnen und sich jeweils um einen positiven Schrittwert erhöhen. Dies hat zur Folge, dass das DataSet negative Zahlen für sich automatisch erhöhende Werte generiert, die nicht zu Konflikten mit den sich automatisch erhöhenden positiven Werten führen, die von der Datenquelle generiert werden. Eine weitere Option wäre, Spalten vom Typ Guid anstelle von AutoIncrement-Spalten zu verwenden. Der Algorithmus, der Guid-Werte generiert, sollte niemals denselben Guid-Wert im DataSet generieren wie der von der Datenquelle generierte. Weitere Informationen zum Definieren von Spalten in einer DataTable finden Sie unter Definieren des Schemas für eine Datentabelle.
Reihenfolge von Einfügungen, Aktualisierungen und Löschvorgängen
In vielen Situationen ist die Reihenfolge, in der die am DataSet vorgenommenen Änderungen zur Datenquelle gesendet werden, sehr wichtig. Wenn beispielsweise ein Primärschlüsselwert für eine vorhandene Zeile aktualisiert und eine neue Zeile mit dem neuen Primärschlüsselwert hinzugefügt wird, muss die Aktualisierung vor der Einfügung verarbeitet werden.
Sie können mit der Select-Methode der DataTable ein DataRow-Array zurückgeben, das nur auf Zeilen mit einem bestimmten RowState verweist. Anschließend können Sie das zurückgegebene DataRow-Array an die Update-Methode des DataAdapter-Objekts übergeben, um die geänderten Zeilen zu verarbeiten. Wenn Sie eine Teilmenge von Zeilen angeben, die aktualisiert werden sollen, können Sie die Reihenfolge steuern, in der Einfügungen, Aktualisierungen und Löschvorgänge verarbeitet werden.
Der folgende Code stellt beispielsweise sicher, dass die gelöschten Zeilen der Tabelle zuerst verarbeitet werden, anschließend die aktualisierten Zeilen und dann die eingefügten Zeilen.
Dim updTable As DataTable = custDS.Tables("Customers")
' First process deletes.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Deleted))
' Next process updates.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent))
' Finally, process inserts.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Added))
[C#]
DataTable updTable = custDS.Tables["Customers"];
// First process deletes.
custDA.Update(updTable.Select(null, null, DataViewRowState.Deleted));
// Next process updates.
custDA.Update(updTable.Select(null, null, DataViewRowState.ModifiedCurrent));
// Finally, process inserts.
custDA.Update(updTable.Select(null, null, DataViewRowState.Added));
Siehe auch
Datenzugriff mit .NET Framework-Datenprovidern | DataSet-Klasse | DataTable-Klasse | OleDbDataAdapter-Klasse | OdbcDataAdapter-Klasse | SqlDataAdapter-Klasse