Exécution d'opérations de traitement par lots à l'aide des DataAdapter

La prise en charge des lots dans ADO.NET permet à un objet DataAdapter de grouper des opérations INSERT, UPDATE et DELETE à partir d'un objet DataSet ou d'un objet DataTable pour le serveur, au lieu d'envoyer les opérations successivement. La réduction du nombre d'allers-retours vers le serveur entraîne généralement des gains de performances importants. Les mises à jour par lots sont prises en charge pour les fournisseurs de données .NET pour SQL Server (System.Data.SqlClient) et Oracle (System.Data.OracleClient).

Lors de la mise à jour d'une base de données avec les modifications d'un objet DataSet dans les versions précédentes d'ADO.NET, la méthode Update d'un objet DataAdapter apportait des mises à jour à la base de données ligne après ligne. Comme elle effectuait une itération dans les lignes de l'objet DataTable spécifié, elle examinait chaque objet DataRow pour voir s'il avait été modifié. Si la ligne avait été modifiée, elle appelait UpdateCommand, InsertCommand ou DeleteCommand en fonction de la valeur de la propriété RowState de cette ligne. Chaque mise à jour de ligne impliquait un aller-retour sur le réseau vers la base de données.

À partir d'ADO.NET 2.0, DbDataAdapter expose une propriété UpdateBatchSize. L'affection d'une valeur entière positive à la propriété UpdateBatchSize a pour effet d'envoyer les mises à jour de la base de données sous la forme de lots de la taille spécifiée. Par exemple, l'affectation de la valeur 10 à la propriété UpdateBatchSize groupe 10 instructions distinctes avant de les soumettre en tant que lot. Avec la valeur 0, l'objet UpdateBatchSize utilise la taille de lot la plus grande que le serveur peut gérer. La valeur 1 désactive les mises à jour par lots car les lignes sont envoyées les unes après les autres.

L'exécution d'un lot très volumineux peut réduire les performances. Vous devez donc tester le paramètre de taille de lot optimal avant d'implémenter votre application.

Utilisation de la propriété UpdateBatchSize

Lorsque les mises à jour par lots sont activées, la valeur UpdatedRowSource ou UpdateCommand doit être affectée à la propriété InsertCommand de DeleteCommand, None et OutputParameters du DataAdapter. Lors de l'exécution d'une mise à jour par lots, les valeurs UpdatedRowSource ou FirstReturnedRecord de la propriété Both de la commande ne sont pas valides.

La procédure suivante illustre l'utilisation de la propriété UpdateBatchSize. La procédure prend deux arguments, un objet DataSet contenant des colonnes représentant les champs ProductCategoryID et Name dans la table Production.ProductCategory, et un entier représentant la taille du lot (nombre de lignes dans le lot). Le code crée un nouvel objet SqlDataAdapter, en définissant ses propriétés UpdateCommand, InsertCommand et DeleteCommand. Le code est basé sur l'hypothèse que l'objet DataSet comporte des lignes modifiées. Il définit la propriété UpdateBatchSize et effectue la mise à jour.

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);
    }
}

Le DataAdapter a deux événements liés aux mises à jour : RowUpdating et RowUpdated. Dans les versions précédentes d'ADO.NET, en cas de désactivation du traitement par lots, chacun de ces événements était généré une fois pour chaque ligne traitée. RowUpdating est généré avant la mise à jour et RowUpdated est généré après la mise à jour de la base de données.

Changements de comportement d'événement avec les mises à jour par lots

Lorsque le traitement par lots est activé, plusieurs lignes sont mises à jour en une seule opération de base de données. Par conséquent, un seul événement RowUpdated se produit pour chaque lot, tandis que l'événement RowUpdating se produit pour chaque ligne traitée. Lorsque le traitement par lots est désactivé, les deux événements sont déclenchés avec un entrelacement de un à un où un événement RowUpdating et un événement RowUpdated se déclenchent pour une ligne, puis un événement RowUpdating et un événement RowUpdated se déclenchent pour la ligne suivante, jusqu'à ce que toutes les lignes aient été traitées.

Accès aux lignes mises à jour

Lorsque le traitement par lots est désactivé, la ligne mise à jour est accessible à l'aide de la propriété Row de la classe RowUpdatedEventArgs.

Lorsque le traitement par lots est activé, un simple événement RowUpdated est généré pour plusieurs lignes. C'est pourquoi la valeur de la propriété Row pour chaque ligne est null. Des événements RowUpdating sont encore générés pour chaque ligne. La méthode CopyToRows de la classe RowUpdatedEventArgs permet d'accéder aux lignes traitées en copiant les références aux lignes dans un tableau. Si aucune ligne n'est traitée, CopyToRows lève une exception ArgumentNullException. Utilisez la propriété RowCount pour retourner le nombre de lignes traitées avant d'appeler la méthode CopyToRows.

Gestion des erreurs de données

L'exécution par lots a le même effet que l'exécution des instructions individuelles. Les instructions sont exécutées dans l'ordre où elles ont été ajoutées au lot. Les erreurs sont gérées de la même manière, que le mode de traitement par lots soit activé ou non. Chaque ligne est traitée séparément. Seules les lignes traitées avec succès dans la base de données sont mises à jour dans l'objet DataRow correspondant à l'objet DataTable.

Le fournisseur de données et le serveur de base de données principal déterminent les constructions SQL prises en charge pour l'exécution par lots. Une exception peut être levée si une instruction non prise en charge est soumise pour exécution.

Voir aussi