Compartir a través de


Eliminación por lotes (C#)

de Scott Mitchell

Descargar PDF

Aprenda a eliminar varios registros de base de datos en una sola operación. En la capa de interfaz de usuario, nos basamos en una GridView mejorada creada en un tutorial anterior. En la capa de acceso a datos encapsulamos las varias operaciones de eliminación dentro de una transacción para asegurarse de que todas las eliminaciones se realicen correctamente o de que todas las eliminaciones se revierten.

Introducción

En el tutorial anterior se ha explorado cómo crear una interfaz de edición por lotes mediante gridView totalmente editable. En situaciones en las que los usuarios suelen editar muchos registros a la vez, una interfaz de edición por lotes requerirá mucho menos postbacks y modificadores de contexto de teclado a mouse, lo que mejora la eficacia del usuario final. Esta técnica es similarmente útil para las páginas en las que es habitual que los usuarios eliminen muchos registros de una sola vez.

Cualquiera que haya usado un cliente de correo electrónico en línea ya está familiarizado con una de las interfaces de eliminación por lotes más comunes: una casilla en cada fila de una cuadrícula con un botón Eliminar todos los elementos activados correspondiente (vea la figura 1). Este tutorial es bastante corto porque ya hemos realizado todo el trabajo duro en tutoriales anteriores para crear la interfaz basada en web y un método para eliminar una serie de registros como una sola operación atómica. En el tutorial Agregar una columna GridView de casillas hemos creado un GridView con una columna de casillas y en el tutorial Envuelto de Modificaciones de Base de Datos dentro de una Transacción hemos creado un método en el BLL que haría uso de una transacción para eliminar un List<T> de ProductID valores. En este tutorial, crearemos y combinaremos nuestras experiencias anteriores para crear un ejemplo de eliminación por lotes en funcionamiento.

Cada fila incluye una casilla

Figura 1: Cada fila incluye una casilla (haga clic para ver la imagen de tamaño completo)

Paso 1: Creación de la interfaz de eliminación de lotes

Puesto que ya hemos creado la interfaz de eliminación por lotes en el tutorial Agregar una columna GridView de casillas , podemos copiarla en BatchDelete.aspx lugar de crearla desde cero. Para empezar, abra la BatchDelete.aspx página en la BatchData carpeta y la CheckBoxField.aspx página de la EnhancedGridView carpeta . Desde la página CheckBoxField.aspx, vaya a la vista Origen y copie el marcado entre las etiquetas <asp:Content>, como se muestra en la Figura 2.

Copiar el marcado declarativo de CheckBoxField.aspx en el Portapapeles

Figura 2: Copiar el marcado declarativo de CheckBoxField.aspx en el Portapapeles (haga clic para ver la imagen de tamaño completo)

A continuación, vaya a la vista de origen en BatchDelete.aspx y pegue el contenido del portapapeles dentro de las <asp:Content> etiquetas. Copie y pegue también el código desde dentro de la clase de código subyacente en CheckBoxField.aspx.cs dentro de la clase de código subyacente en BatchDelete.aspx.cs (el controlador de eventos para el botón DeleteSelectedProducts, el método Click y los controladores de eventos para los botones ToggleCheckState y Click). Después de copiar este contenido, la clase de código subyacente de la BatchDelete.aspx página debe contener el código siguiente:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class BatchData_BatchDelete : System.Web.UI.Page
{
    protected void DeleteSelectedProducts_Click(object sender, EventArgs e)
    {
        bool atLeastOneRowDeleted = false;
        // Iterate through the Products.Rows property
        foreach (GridViewRow row in Products.Rows)
        {
            // Access the CheckBox
            CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
            if (cb != null && cb.Checked)
            {
                // Delete row! (Well, not really...)
                atLeastOneRowDeleted = true;
                // First, get the ProductID for the selected row
                int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
                // "Delete" the row
                DeleteResults.Text += string.Format
                    ("This would have deleted ProductID {0}<br />", productID);
                //... To actually delete the product, use ...
                //ProductsBLL productAPI = new ProductsBLL();
                //productAPI.DeleteProduct(productID);
                //............................................
            }
        }
        // Show the Label if at least one row was deleted...
        DeleteResults.Visible = atLeastOneRowDeleted;
    }
    private void ToggleCheckState(bool checkState)
    {
        // Iterate through the Products.Rows property
        foreach (GridViewRow row in Products.Rows)
        {
            // Access the CheckBox
            CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
            if (cb != null)
                cb.Checked = checkState;
        }
    }
    protected void CheckAll_Click(object sender, EventArgs e)
    {
        ToggleCheckState(true);
    }
    protected void UncheckAll_Click(object sender, EventArgs e)
    {
        ToggleCheckState(false);
    }
}

Después de copiar el marcado declarativo y el código fuente, tómese un momento para probar BatchDelete.aspx mediante un navegador. Debería ver una vista de cuadrícula que enumera los diez primeros productos con cada fila mostrando el nombre, la categoría y el precio del producto, junto con una casilla. Debe haber tres botones: Comprobar todo, Desactivar todo y Eliminar productos seleccionados. Al hacer clic en el botón Seleccionar todas, se activan todas las casillas, mientras que Deseleccionar todas desactiva todas las casillas. Al hacer clic en Eliminar productos seleccionados se muestra un mensaje que muestra los ProductID valores de los productos seleccionados, pero no elimina realmente los productos.

La interfaz de CheckBoxField.aspx se ha movido a BatchDeleting.aspx

Figura 3: La interfaz de CheckBoxField.aspx se ha movido a BatchDeleting.aspx (haga clic para ver la imagen de tamaño completo)

Paso 2: Eliminar los productos comprobados mediante transacciones

Con la interfaz de eliminación por lotes copiada correctamente en BatchDeleting.aspx, todo lo que queda es actualizar el código para que el botón Eliminar productos seleccionados elimine los productos comprobados mediante el DeleteProductsWithTransaction método de la ProductsBLL clase . Este método, agregado en el tutorial Envolviendo modificaciones de base de datos dentro de una transacción, toma como entrada un List<T> de ProductID valores y elimina cada uno de los correspondientes ProductID dentro del ámbito de una transacción.

El DeleteSelectedProducts controlador de eventos Button usa Click actualmente el siguiente foreach bucle para recorrer en iteración cada fila de GridView:

// Iterate through the Products.Rows property
foreach (GridViewRow row in Products.Rows)
{
    // Access the CheckBox
    CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
    if (cb != null && cb.Checked)
    {
        // Delete row! (Well, not really...)
        atLeastOneRowDeleted = true;
        // First, get the ProductID for the selected row
        int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
        // "Delete" the row
        DeleteResults.Text += string.Format
            ("This would have deleted ProductID {0}<br />", productID);
        //... To actually delete the product, use ...
        //ProductsBLL productAPI = new ProductsBLL();
        //productAPI.DeleteProduct(productID);
        //............................................
    }
}

Para cada fila, se hace referencia mediante programación al ProductSelector control web CheckBox. Si está activada, la fila s ProductID se recupera de la DataKeys colección y la DeleteResults propiedad Label s Text se actualiza para incluir un mensaje que indica que la fila se seleccionó para su eliminación.

El código anterior no elimina realmente ningún registro, ya que la llamada al método ProductsBLL de la clase Delete está comentada. Si esta lógica de eliminación se aplicara, el código eliminaría los productos, pero no dentro de una operación atómica. Es decir, si las primeras eliminaciones de la secuencia se realizaron correctamente, pero se produjo un error posterior (quizá debido a una infracción de restricción de clave externa), se produciría una excepción, pero esos productos ya eliminados se eliminarían.

Para garantizar la atomicidad, es necesario usar en su lugar el método de la ProductsBLL clase s DeleteProductsWithTransaction . Dado que este método acepta una lista de ProductID valores, primero es necesario compilar esta lista desde la cuadrícula y, a continuación, pasarla como parámetro. Primero se crea una instancia de un List<T> de tipo int. Dentro del foreach bucle, es necesario agregar los valores de productos ProductID seleccionados a este List<T>. Después del bucle, este List<T> debe pasarse al método de la clase ProductsBLLDeleteProductsWithTransaction. Actualice el DeleteSelectedProducts controlador de eventos Button s Click con el código siguiente:

protected void DeleteSelectedProducts_Click(object sender, EventArgs e)
{
    // Create a List to hold the ProductID values to delete
    System.Collections.Generic.List<int> productIDsToDelete = 
        new System.Collections.Generic.List<int>();
    // Iterate through the Products.Rows property
    foreach (GridViewRow row in Products.Rows)
    {
        // Access the CheckBox
        CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
        if (cb != null && cb.Checked)
        {
            // Save the ProductID value for deletion
            // First, get the ProductID for the selected row
            int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
            // Add it to the List...
            productIDsToDelete.Add(productID);
            // Add a confirmation message
            DeleteResults.Text += string.Format
                ("ProductID {0} has been deleted<br />", productID);
        }
    }
    // Call the DeleteProductsWithTransaction method and show the Label 
    // if at least one row was deleted...
    if (productIDsToDelete.Count > 0)
    {
        ProductsBLL productAPI = new ProductsBLL();
        productAPI.DeleteProductsWithTransaction(productIDsToDelete);
        DeleteResults.Visible = true;
        // Rebind the data to the GridView
        Products.DataBind();
    }
}

El código actualizado crea un List<T> de tipo int (productIDsToDelete) y lo rellena con los ProductID valores que se van a eliminar. Después del foreach bucle, si hay al menos un producto seleccionado, se llama al método de la ProductsBLL clase s DeleteProductsWithTransaction y se pasa esta lista. La DeleteResults etiqueta también se muestra y los datos se vuelven a enlazar al GridView, para que los registros recién eliminados ya no aparezcan como filas en la cuadrícula.

En la figura 4 se muestra GridView después de seleccionar una serie de filas para su eliminación. En la figura 5 se muestra la pantalla inmediatamente después de hacer clic en el botón Eliminar productos seleccionados. Tenga en cuenta que en la figura 5 los ProductID valores de los registros eliminados se muestran en la etiqueta situada debajo de GridView y esas filas ya no están en GridView.

Se eliminarán los productos seleccionados.

Figura 4: Se eliminarán los productos seleccionados (haga clic para ver la imagen de tamaño completo)

Los valores productID de productos eliminados se enumeran debajo de GridView.

Figura 5: Los valores de productos ProductID eliminados se enumeran debajo de GridView (haga clic para ver la imagen de tamaño completo)

Nota:

Para probar la atomicidad del DeleteProductsWithTransaction método, agregue manualmente una entrada para un producto en la Order Details tabla e intente eliminar ese producto (junto con otros). Recibirá una infracción de restricción de clave externa al intentar eliminar el producto con un pedido asociado, pero tenga en cuenta cómo se revierten las otras eliminaciones de productos seleccionados.

Resumen

La creación de una interfaz de eliminación por lotes implica agregar un GridView con una columna de casillas de verificación y un control de botón web que, al hacer clic, eliminará todas las filas seleccionadas como una operación atómica única. En este tutorial hemos creado una interfaz de este tipo combinando el trabajo realizado en dos tutoriales anteriores, Agregar una columna GridView de casillas y Ajustar modificaciones de base de datos dentro de una transacción. En el primer tutorial, creamos un GridView con una columna de casillas de verificación y, en el último, implementamos un método en el BLL que, al pasar un List<T> de ProductID valores, los elimina todos dentro del ámbito de una transacción.

En el siguiente tutorial, crearemos una interfaz para realizar inserciones por lotes.

¡Feliz programación!

Acerca del autor

Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha estado trabajando con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 en 24 horas. Se puede contactar con él en mitchell@4GuysFromRolla.com.

Agradecimientos especiales a

Esta serie de tutoriales contó con la revisión de muchos revisores que fueron de gran ayuda. Los revisores principales de este tutorial fueron Hilton Giesenow y Teresa Murphy. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, mándame un mensaje a mitchell@4GuysFromRolla.com.