Compartir a través de


Realizar operaciones por lotes utilizando DataAdapters

La compatibilidad con las operaciones por lotes en ADO.NET permite que un DataAdapter agrupe operaciones INSERT, UPDATE y DELETE desde un DataSet o una DataTable al servidor, en lugar de enviar las operaciones de una en una. La reducción del número de viajes de ida y vuelta (round trip) al servidor tiene como resultado una mejora considerable del rendimiento. Las actualizaciones por lotes son compatibles con los proveedores de datos de .NET para SQL Server (System.Data.SqlClient) y Oracle (System.Data.OracleClient).

Al actualizar una base de datos con modificaciones de un DataSet en versiones anteriores de ADO.NET, el método Update de un DataAdapter realizaba actualizaciones de las filas de la base de datos de una en una. A medida que recorría las filas de la DataTable especificada, examinaba cada DataRow para ver si se había modificado. Si se había modificado la fila, llamaba al UpdateCommand, InsertCommand o DeleteCommand apropiado, en función del valor de propiedad RowState de la fila. Cada actualización de una fila implicaba un viaje de ida y vuelta (round trip) a la base de datos.

A partir de ADO.NET 2.0, DbDataAdapter expone una propiedad UpdateBatchSize. Si se establece el UpdateBatchSize en un valor entero positivo, se producen actualizaciones en la base de datos que se envían como lotes del tamaño especificado. Por ejemplo, si se establece el UpdateBatchSize en 10, se agrupan 10 instrucciones separadas y se envían en un único lote. Si se establece el UpdateBatchSize en 0, el DataAdapter utilizará el mayor tamaño de lote admitido por el servidor. Si se establece el valor en 1, se deshabilitan las actualizaciones por lotes y las filas se envían de una en una.

Si se ejecuta un lote demasiado grande, el rendimiento podría verse afectado. Por tanto, es conveniente realizar pruebas a fin de determinar el valor óptimo del tamaño del lote antes de implementar la aplicación.

Utilizar la propiedad UpdateBatchSize

Al habilitar las actualizaciones por lotes, el valor de propiedad UpdatedRowSource de UpdateCommand, InsertCommand y DeleteCommand del DataAdapter debe establecerse en None o OutputParameters. Al realizar una actualización por lotes, el valor UpdatedRowSource o FirstReturnedRecord de la propiedad Both del comando no es válido.

En el siguiente procedimiento se muestra cómo se utiliza la propiedad UpdateBatchSize. En el procedimiento se toman dos argumentos, un objeto DataSet con columnas que representan los campos ProductCategoryID y Name de la tabla Production.ProductCategory, y un entero que representa el tamaño del lote (el número de filas). El código crea un objeto SqlDataAdapter nuevo y se establecen las propiedades UpdateCommand, InsertCommand y DeleteCommand. En el código se supone que el objeto DataSet tiene filas modificadas. Se establece la propiedad UpdateBatchSize y se ejecuta la actualización.

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

DataAdapter tiene dos eventos relacionados con la actualización: RowUpdating y RowUpdated. En las versiones anteriores de ADO.NET, cuando se deshabilita el procesamiento por lotes, cada uno de estos eventos se genera una vez para cada fila procesada. RowUpdating se genera antes de que tenga lugar la actualización y RowUpdated se genera una vez completada la actualización de la base de datos.

Cambios en el comportamiento de eventos con actualizaciones por lotes

Si se habilita el procesamiento por lotes, se actualizan varias filas en una única operación de base de datos. Por tanto, solo se produce un evento RowUpdated para cada lote, mientras que el evento RowUpdating se produce para cada fila procesada. Si se deshabilita el procesamiento por lotes, los dos eventos se activan con entrelazado individualizado, donde los eventos RowUpdating y RowUpdated se activan para una fila y, a continuación, se activan los eventos RowUpdating y RowUpdated para la siguiente fila, hasta que se hayan procesado todas las filas.

Obtener acceso a filas actualizadas

Si se deshabilita el procesamiento por lotes, se puede obtener acceso a la fila que se está actualizando mediante la propiedad Row de la clase RowUpdatedEventArgs.

Cuando se habilita el procesamiento por lotes, se genera un único evento RowUpdated para varias filas. Por tanto, el valor de la propiedad Row para cada fila es nulo. Aun así, los eventos RowUpdating se generarán para cada fila. El método CopyToRows de la clase RowUpdatedEventArgs permite obtener acceso a las filas procesadas al copiar referencias a las mismas en una matriz. Si no se está procesando ninguna fila, CopyToRows inicia una ArgumentNullException. Utilice la propiedad RowCount para devolver el número de filas procesadas antes de llamar al método CopyToRows.

Controlar errores de datos

La ejecución por lotes tiene el mismo efecto que la ejecución de cada instrucción por separado. Las instrucciones se ejecutan en el mismo orden en el que se agregaron al lote. Los errores se controlan de la misma forma en el modo de procesamiento por lotes que cuando éste se encuentra deshabilitado. Cada fila se procesa por separado. Solo aquellas filas procesadas correctamente en la base de datos se actualizarán en la DataRow correspondiente dentro de la DataTable.

El proveedor de datos y el servidor de bases de datos back-end determinan qué construcciones SQL son compatibles para la ejecución por lotes. Es posible que se inicie una excepción si se envía una instrucción no compatible para su ejecución.

Consulte también