批量删除64

简介

前一篇教程 探讨了如何使用一个完全可编辑的 GridView 来创建批量编辑界面。在用户通常一次编辑多条记录的情况下,批量编辑界面需要更少的回传以及键盘到鼠标的上下文切换,从而提高了终端用户的效率。同理,对于常见的用户一次删除多条记录的页面,该技术也很有用。

使用过在线电子邮件客户端的人都对这种最常见的批量删除界面很熟悉:网格里的每一行都有一个复选框,还有一个对应的“Delete All Checked Items” 按钮(见图1 )。本教程内容很少,因为我们已在前面教程中完成了所有困难的工作:创建基于web 的界面和作为一个原子操作删除多条记录的方法。在为 GridView控件添加一列 Checkbox 控件 教程中,我们创建了一个包含复选框列的GridView ,在事务中封装数据库修改 教程中,我们在 BLL 中创建了一个方法,该方法使用事务来删除List<T> 的 ProductID 值。在本教程中,我们将借鉴并整合前面的经验来创建一个批量删除示例。

图1 : 每行都有一个复选框

步骤1 : 创建批量删除界面

由于我们在为 GridView控件添加一列 Checkbox 控件 教程中已经创建了批量删除界面 , 我们可简单地将其复制到 BatchDelete.aspx , 而不用重新创建。首先,打开 BatchData 文件夹中的 BatchDelete.aspx 页面,以及 EnhancedGridView 文件夹中的 CheckBoxField.aspx 页面。从 CheckBoxField.aspx 页面中,转至 Source 视图,并复制 <asp:Content> 标签之间的标记,如图 2 所示。

图2 : 将 CheckBoxField.aspx 的声明式标记复制到剪贴板

然后 , 进入BatchDelete.aspx 的 Source 视图 , 将剪贴板中的内容粘贴到 <asp:Content> 标签内。同理,将 CheckBoxField.aspx.cs 中代码文件类的代码复制到 BatchDelete.aspx.cs 的代码文件类中( DeleteSelectedProducts 按钮的 Click event handler 、 ToggleCheckState 方法以及 CheckAll 和 UncheckAll 按钮的 Click event handler )。复制完成后 ,BatchDelete.aspx 页面的代码文件类应包含下面的代码 :

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

复制完声明式标记和源代码后 , 花几分钟通过浏览器测试一下 BatchDelete.aspx 。应该看到一个列出了前 10 类产品的 GridView ,每行列出产品的名称、类别、价格和一个复选框。同时还应该有三个按钮: “Check All” 、 “Uncheck All” 和 “Delete Selected Products” 。单击 “Check All” 按钮会选中所有的复选框,而 “Uncheck All” 将取消选中所有的复选框。单击 “Delete Selected Products” 将显示一个消息,列出所选产品的 ProductID 值,但不会真的删除该产品。

图3 : 界面从 CheckBoxField.aspx 移到 BatchDeleting.aspx

步骤2 : 使用事务删除选中的产品

成功将批量删除界面复制到 BatchDeleting.aspx 后 , 剩下的事情是更新代码 , 以便 “Delete Selected Products” 按钮通过ProductsBLL 类的DeleteProductsWithTransaction 方法来删除选中的产品。这个 在事务中封装数据库修改 教程中增加的方法接受 List<T> 的 ProductID 值作为其输入值,并在事务范围内删除对应的每个 ProductID 。

DeleteSelectedProducts 按钮的Click event handler 目前使用以下For Each 循环来叠代各个 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); 
        //............................................ 
    } 
}

对于每一行,以编程的方式引用ProductSelector CheckBox Web 控件。如果它被选中,从 DataKeys 集获取该行的ProductID ,然后更新 DeleteResults Label 的 Text 属性以包含指示选择删除该行的消息。

上面的代码不会真的删除任何记录,因为我们注释了对ProductsBLL 类的 Delete 方法的调用。不过就算实际运用了这些删除逻辑,代码虽然可以删除产品,但它不是在原子操作内进行的。也就是说,如果序列中的前几个删除成功,后面的一个删除失败(可能是因违反外键约束所致),将抛出一个异常,但那些已被删除的产品仍然会保持删除。

为保证使用原子操作,我们需要使用ProductsBLL 类的DeleteProductsWithTransaction 方法。由于该方法接受一系列的ProductID 值,我们首先要在网格中编译该列表,然后将它作为参数进行传递。首先创建一个int 类型的 List<T> 实例。在 foreach 循环里,我们需要将所选产品的ProductID 值添加到该 List<T> 。循环结束后,必须将该 List<T> 传递给 ProductsBLL 类的DeleteProductsWithTransaction 方法。使用下面的代码更新DeleteSelectedProducts 按钮的Click event handler :

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

更新的代码创建一个int 类型的 List<T> (productIDsToDelete) ,并在其内填充要删除的ProductID 值。foreach 循环结束后,如果至少选择了一个产品,则调用ProductsBLL 类的DeleteProductsWithTransaction 方法,并传递该列表。也会显示DeleteResults Label ,数据重新绑定到GridView (这样,刚删除的记录就不再出现在网格中)。

图 4 显示的是选择删除的几行后的 GridView 。图 5 显示的是单击 “Delete Selected Products” 按钮后立即显示的屏幕。注意,在图 5 中,被删除记录的 ProductID 值显示在 GridView 下面的 Label 中,这些记录不再显示在 GridView 中。

图4 : 选中的产品将被删除

图5 : 在 GridView 的下面列出了被删除产品的 ProductID

注意 : 为验证 DeleteProductsWithTransaction 方法的原子操作 , 可在Order Details 表中手动添加某个产品的条目 , 然后尝试 ( 与其它产品一起 ) 删除该产品。在试图删除有相关订单的产品时,将会收到违反外键约束的信息,不过要留意其它所选产品的删除是如何回退的。

小结

创建一个批量删除界面 , 需要添加一个有复选框列的 GridView , 以及一个 Web 按钮控件 , 单击该按钮时 , 将在一个原子操作中删除所有选中的记录。在本教程中,我们创建的这样一个界面整合了前面两篇教程( 为 GridView 控件添加一列 Checkbox 控件 和 在事务里封装数据库修改 )中所做的工作。在第一篇教程中,我们创建了一个有复选框列的 GridView ,在后面一篇教程中,我们在 BLL 中实现了一个方法,该方法传递 List(Of T) 的 ProductID 值,并在事务范围内删除它们。

在下一篇教程中,我们将创建一个执行批量插入的界面。

快乐编程!

posted @ 2016-05-02 00:30  迅捷之风  阅读(150)  评论(0编辑  收藏  举报