Ausführen von Batchvorgängen mit "DataAdapters"

Durch Batch-Unterstützung in ADO.NET kann ein DataAdapter die Operationen INSERT, UPDATE und DELETE aus einem DataSet oder einer DataTable an einen Server zusammenfassen, anstatt nur jeweils eine Operation senden zu können. Durch das Reduzieren der Anzahl von Roundtrips zum Server kann im Allgemeinen die Leistung beträchtlich gesteigert werden. Batchupdates werden für die .NET-Datenanbieter für SQL Server (System.Data.SqlClient) und Oracle (System.Data.OracleClient) unterstützt.

Beim Aktualisieren einer Datenbank mit Änderungen aus einem DataSet wurde die Datenbank in früheren Versionen von ADO.NET mit der Update-Methode eines DataAdapter zeilenweise aktualisiert. Dabei wurden die Zeilen in der angegebenen DataTable durchlaufen, wobei jede DataRow auf Änderungen geprüft wurde. Wenn die Zeile geändert worden war, wurde, abhängig vom Wert der UpdateCommand-Eigenschaft für die Zeile, der entsprechende InsertCommand, DeleteCommand oder RowState aufgerufen. Für jede Zeilenaktualisierung war ein Netzwerkroundtrip zur Datenbank erforderlich.

Beginnend mit ADO.NET 2.0 wird vom DbDataAdapter-Objekt eine UpdateBatchSize-Eigenschaft verfügbar gemacht. Wenn die UpdateBatchSize-Eigenschaft auf einen positiven ganzzahligen Wert festgelegt wird, werden Updates der Datenbank als Batches mit der angegebenen Größe gesendet. Wenn z. B. für UpdateBatchSize der Wert 10 festgelegt wird, bedeutet dies, dass jeweils 10 Anweisungen in einer Gruppe zusammengefasst und zusammen als Batch gesendet werden. Wird dagegen für UpdateBatchSize 0 festgelegt, verwendet der DataAdapter die größtmöglichen Batches, die der Server unterstützt. Bei einem Wert von 1 werden Batchupdates deaktiviert, weil die Zeilen einzeln nacheinander gesendet werden.

Die Ausführung eines extrem großen Batches könnte die Leistung verringern. Daher sollten Sie die Einstellung für eine optimale Batchgröße vor der Implementierung Ihrer Anwendung austesten.

Verwenden der "UpdateBatchSize"-Eigenschaft

Wenn Batchupdates aktiviert sind, sollte der UpdatedRowSource-Eigenschaftswert von UpdateCommand, InsertCommand und DeleteCommand des DataAdapter auf None oder OutputParameters festgelegt werden. Beim Update eines Batches sind der UpdatedRowSource-Wert und der FirstReturnedRecord-Wert der Both-Eigenschaft des Befehls ungültig.

In der folgenden Prozedur wird die Verwendung der UpdateBatchSize-Eigenschaft veranschaulicht. Die Prozedur akzeptiert zwei Argumente: ein DataSet-Objekt mit Spalten für die Felder ProductCategoryID und Name in der Production.ProductCategory-Tabelle und eine ganze Zahl, die die Batchgröße (die Anzahl der Zeilen im Batch) darstellt. Der Code erstellt ein neues SqlDataAdapter-Objekt und legt dessen Eigenschaften UpdateCommand, InsertCommand und DeleteCommand fest. Der Code geht davon aus, dass das DataSet-Objekt geänderte Zeilen aufweist. Er legt einen Wert für die UpdateBatchSize-Eigenschaft fest und führt das Update aus.

Public Sub BatchUpdate( _  
  ByVal dataTable As DataTable, ByVal batchSize As Int32)  
    ' Assumes GetConnectionString() returns a valid connection string.  
    Dim connectionString As String = GetConnectionString()  
  
    ' Connect to the AdventureWorks database.  
    Using connection As New SqlConnection(connectionString)  
        ' Create a SqlDataAdapter.  
        Dim adapter As New SqlDataAdapter()  
  
        'Set the UPDATE command and parameters.  
        adapter.UpdateCommand = New SqlCommand( _  
          "UPDATE Production.ProductCategory SET " _  
          & "Name=@Name WHERE ProductCategoryID=@ProdCatID;", _  
          connection)  
        adapter.UpdateCommand.Parameters.Add("@Name", _  
          SqlDbType.NVarChar, 50, "Name")  
        adapter.UpdateCommand.Parameters.Add("@ProdCatID",  _  
          SqlDbType.Int, 4, " ProductCategoryID ")  
        adapter.UpdateCommand.UpdatedRowSource = _  
          UpdateRowSource.None  
  
        'Set the INSERT command and parameter.  
        adapter.InsertCommand = New SqlCommand( _  
          "INSERT INTO Production.ProductCategory (Name) VALUES (@Name);", _  
  connection)  
        adapter.InsertCommand.Parameters.Add("@Name", _  
          SqlDbType.NVarChar, 50, "Name")  
        adapter.InsertCommand.UpdatedRowSource = _  
          UpdateRowSource.None  
  
        'Set the DELETE command and parameter.  
        adapter.DeleteCommand = New SqlCommand( _  
          "DELETE FROM Production.ProductCategory " _  
          & "WHERE ProductCategoryID=@ProdCatID;", connection)  
        adapter.DeleteCommand.Parameters.Add("@ProdCatID", _  
           SqlDbType.Int, 4, " ProductCategoryID ")  
        adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None  
  
        ' Set the batch size.  
        adapter.UpdateBatchSize = batchSize  
  
        ' Execute the update.  
        adapter.Update(dataTable)  
    End Using  
End Sub  
public static void BatchUpdate(DataTable dataTable,Int32 batchSize)  
{  
    // Assumes GetConnectionString() returns a valid connection string.  
    string connectionString = GetConnectionString();  
  
    // Connect to the AdventureWorks database.  
    using (SqlConnection connection = new
      SqlConnection(connectionString))  
    {  
  
        // Create a SqlDataAdapter.  
        SqlDataAdapter adapter = new SqlDataAdapter();  
  
        // Set the UPDATE command and parameters.  
        adapter.UpdateCommand = new SqlCommand(  
            "UPDATE Production.ProductCategory SET "  
            + "Name=@Name WHERE ProductCategoryID=@ProdCatID;",
            connection);  
        adapter.UpdateCommand.Parameters.Add("@Name",
           SqlDbType.NVarChar, 50, "Name");  
        adapter.UpdateCommand.Parameters.Add("@ProdCatID",
           SqlDbType.Int, 4, "ProductCategoryID");  
         adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;  
  
        // Set the INSERT command and parameter.  
        adapter.InsertCommand = new SqlCommand(  
            "INSERT INTO Production.ProductCategory (Name) VALUES (@Name);",
            connection);  
        adapter.InsertCommand.Parameters.Add("@Name",
          SqlDbType.NVarChar, 50, "Name");  
        adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;  
  
        // Set the DELETE command and parameter.  
        adapter.DeleteCommand = new SqlCommand(  
            "DELETE FROM Production.ProductCategory "  
            + "WHERE ProductCategoryID=@ProdCatID;", connection);  
        adapter.DeleteCommand.Parameters.Add("@ProdCatID",
          SqlDbType.Int, 4, "ProductCategoryID");  
        adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None;  
  
        // Set the batch size.  
        adapter.UpdateBatchSize = batchSize;  
  
        // Execute the update.  
        adapter.Update(dataTable);  
    }  
}  

Die DataAdapter-Klasse verfügt über zwei updaterelevante Ereignisse: RowUpdating und RowUpdated. In älteren Versionen von ADO.NET werden diese Ereignisse jeweils einmal für jede verarbeitete Zeile generiert, wenn die Batchverarbeitung deaktiviert wurde. Das RowUpdating-Ereignis wird vor dem Update erstellt, und das RowUpdated-Ereignis wird nach Abschluss des Datenbankupdates erstellt.

Änderungen im Ereignisverhalten durch Batchupdates

Bei aktivierter Batchverarbeitung werden mehrere Zeilen in einer einzigen Datenbankoperation aktualisiert. Deshalb findet pro Batch nur ein RowUpdated-Ereignis statt, wohingegen das RowUpdating-Ereignis für jede verarbeitete Zeile auftritt. Bei deaktivierter Batchverarbeitung werden die beiden Ereignisse mit Einzelverschachtelung ausgelöst, wobei für jede Zeile zunächst ein RowUpdating-Ereignis und dann ein RowUpdated-Ereignis ausgelöst wird. Diese Abfolge der Auslösung von RowUpdating-Ereignissen und RowUpdated-Ereignissen setzt sich so lange fort, bis alle Zeilen verarbeitet wurden.

Zugreifen auf aktualisierte Zeilen

Bei deaktivierter Batchverarbeitung kann auf die zu aktualisierende Zeile mit der Row-Eigenschaft der RowUpdatedEventArgs-Klasse zugegriffen werden.

Bei aktivierter Batchverarbeitung wird für mehrere Zeilen ein einziges RowUpdated-Ereignis generiert. Daher ist der Wert der Row-Eigenschaft in jeder Zeile NULL. Es werden weiterhin RowUpdating-Ereignisse für jede Zeile generiert. Mit der CopyToRows-Methode der RowUpdatedEventArgs-Klasse können Sie auf die verarbeiteten Zeilen zugreifen, indem Sie Verweise auf die Zeilen in ein Array kopieren. Wenn keine Zeilen verarbeitet werden, löst CopyToRows eine ArgumentNullException aus. Verwenden Sie die RowCount-Eigenschaft, um vor dem Aufruf der CopyToRows-Methode die Anzahl der verarbeiteten Zeilen zurückzugeben.

Behandeln von Datenfehlern

Eine Batchausführung hat dieselben Auswirkungen wie die Ausführung einzelner Anweisungen. Anweisungen werden in der Reihenfolge ausgeführt, in der sie dem Batch hinzugefügt wurden. Fehler werden im Batchmodus in derselben Weise behandelt wie bei deaktiviertem Batchmodus. Jede Zeile wird einzeln verarbeitet. Nur Zeilen, die erfolgreich in der Datenbank verarbeitet wurden, werden in der entsprechenden DataRow innerhalb der DataTable aktualisiert.

Der Datenanbieter und der Back-End-Datenbankserver bestimmen, welche SQL-Konstrukte für die Batchausführung unterstützt werden. Wenn eine nicht unterstützte Anweisung ausgeführt werden soll, wird möglicherweise eine Ausnahme ausgelöst.

Weitere Informationen