批量删除 (VB)

作者 :斯科特·米切尔

下载 PDF

了解如何在单个作中删除多个数据库记录。 在 UI 层中,我们基于之前教程中创建的增强型 GridView 进行构建。 在数据访问层中,我们会在事务中将多个删除操作封装起来,以确保所有删除都成功或确保回滚所有删除。

介绍

前面的教程探讨了如何使用完全可编辑的 GridView 创建批处理编辑界面。 在用户经常同时编辑许多记录的情况下,批量编辑界面需要更少的回传和减少切换上下文从键盘到鼠标,从而提高用户效率。 此方法同样适用于用户一次性删除许多记录的页面。

使用联机电子邮件客户端的任何人都可以熟悉最常见的批量删除接口之一:网格中每一行中的复选框,其中包含相应的“删除所有选中项目”按钮(请参阅图 1)。 本教程相当简短,因为我们在之前的教程中已完成所有艰苦的工作,包括创建网页界面以及实现一种将一系列记录作为一个原子操作删除的方法。 在“添加 GridView 复选框列”教程中,我们创建了一个包含复选框列的 GridView,并在“通过事务包装数据库修改”教程中,在业务逻辑层(BLL)中创建了一个方法,该方法使用事务来删除List<T>ProductID的值。 在本教程中,我们将基于前面的体验构建和合并,以创建一个工作批处理删除示例。

每行都包含一个复选框

图 1:每行都包含一个复选框(单击以查看全尺寸图像

步骤 1:创建批处理删除接口

由于我们已在 “添加复选框的 GridView 列 ”教程中创建批处理删除接口,因此我们只需将其 BatchDelete.aspx 复制到而不是从头开始创建它。 首先打开 BatchDelete.aspx 文件夹中的页面 BatchDataCheckBoxField.aspx 文件夹中的页面 EnhancedGridView 。 在CheckBoxField.aspx页面上访问“源”视图,并复制位于<asp:Content>标签之间的标记,如图 2 所示。

将CheckBoxField.aspx的声明性标记复制到剪贴板

图 2:将声明性标记 CheckBoxField.aspx 复制到剪贴板(单击以查看全尺寸图像

接下来,进入BatchDelete.aspx的“源视图”,然后将剪贴板中的内容粘贴到<asp:Content>标签中。 此外,将代码从后台代码类中的CheckBoxField.aspx.vb复制并粘贴到后台代码类中的BatchDelete.aspx.vb(即DeleteSelectedProducts按钮的Click事件处理程序、ToggleCheckState方法,以及ClickCheckAll按钮的UncheckAll事件处理程序)。 复制此内容后, BatchDelete.aspx 页面的代码隐藏类应包含以下代码:

Partial Class BatchData_BatchDelete
    Inherits System.Web.UI.Page
    Protected Sub DeleteSelectedProducts_Click(sender As Object, e As EventArgs) _
        Handles DeleteSelectedProducts.Click
        
        Dim atLeastOneRowDeleted As Boolean = False
        ' Iterate through the Products.Rows property
        For Each row As GridViewRow In Products.Rows
            ' Access the CheckBox
            Dim cb As CheckBox = row.FindControl("ProductSelector")
            If cb IsNot Nothing AndAlso cb.Checked Then
                ' Delete row! (Well, not really...)
                atLeastOneRowDeleted = True
                ' First, get the ProductID for the selected row
                Dim productID As Integer = _
                    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 ...
                ' Dim productAPI As New ProductsBLL
                ' productAPI.DeleteProduct(productID)
                '............................................
            End If
        Next
        ' Show the Label if at least one row was deleted...
        DeleteResults.Visible = atLeastOneRowDeleted
    End Sub
    Private Sub ToggleCheckState(ByVal checkState As Boolean)
        ' Iterate through the Products.Rows property
        For Each row As GridViewRow In Products.Rows
            ' Access the CheckBox
            Dim cb As CheckBox = row.FindControl("ProductSelector")
            If cb IsNot Nothing Then
                cb.Checked = checkState
            End If
        Next
    End Sub
    Protected Sub CheckAll_Click(sender As Object, e As EventArgs) _
        Handles CheckAll.Click
        ToggleCheckState(True)
    End Sub
    Protected Sub UncheckAll_Click(sender As Object, e As EventArgs) _
        Handles UncheckAll.Click
        ToggleCheckState(False)
    End Sub
End Class

在复制声明性标记和源代码后,请花点时间通过浏览器查看并测试 BatchDelete.aspx。 您应该会看到一个 GridView,其中列出前 10 个产品。每行显示产品名称、类别和价格,并附有一个复选框。 应有三个按钮:“全选”、“取消全选”和“删除所选产品”。 单击“全部选中”按钮将选中所有复选框,“全部取消选中”将清除所有复选框。 单击“删除所选产品”会显示一条消息,其中列出了 ProductID 所选产品的值,但实际上不会删除产品。

已将来自CheckBoxField.aspx的接口移动到BatchDeleting.aspx

图 3:已将接口 CheckBoxField.aspx 移动到 BatchDeleting.aspx单击以查看全尺寸图像

第 2 步:使用事务删除已检查的商品

当批量删除接口成功复制到 BatchDeleting.aspx 后,剩下的任务仅仅是更新代码,以便“删除所选产品”按钮使用 DeleteProductsWithTransaction 类中的 ProductsBLL 方法删除已检查的产品。 此方法在事务教程中的包装数据库修改中添加,接受其输入List(Of T)ProductID值并删除事务范围内的每个对应ProductID项。

DeleteSelectedProducts Button s Click 事件处理程序当前使用以下For Each循环循环遍历每个 GridView 行:

' Iterate through the Products.Rows property
For Each row As GridViewRow In Products.Rows
    ' Access the CheckBox
    Dim cb As CheckBox = row.FindControl("ProductSelector")
    If cb IsNot Nothing AndAlso cb.Checked Then
        ' Delete row! (Well, not really...)
        atLeastOneRowDeleted = True
        ' First, get the ProductID for the selected row
        Dim productID As Integer = _
            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 ...
        ' Dim productAPI As New ProductsBLL
        ' productAPI.DeleteProduct(productID)
        '............................................
    End If
Next

对于每一行,会以编程方式引用 ProductSelector CheckBox Web 控件。 如果选中此项,则会从ProductID集合中检索DataKeys行,并且DeleteResults标签的Text属性将被更新,以包含一条消息,指示该行已被选择用于删除。

上述代码实际上不会删除任何记录,因为对类ProductsBLL调用方法Delete被注释掉。如果应用此删除逻辑,代码将删除产品,但不在原子操作中进行。 也就是说,如果序列中的前几个删除成功,但后来的一个删除失败(可能是由于外键约束冲突),则会引发异常,但那些已经删除的产品将保持删除状态。

为了保证原子性,我们需要改用 ProductsBLL 类中的 DeleteProductsWithTransaction 方法。 由于此方法接受值列表 ProductID ,因此我们需要首先从网格编译此列表,然后将其作为参数传递。 我们首先创建一个 List(Of T) 类型的 Integer实例。 在 For Each 循环中,我们需要将所选产品 ProductID 值添加到此 List(Of T)。 循环后,必须将List(Of T)传递给ProductsBLL类的DeleteProductsWithTransaction方法。 使用以下代码更新DeleteSelectedProducts按钮的Click事件处理程序:

Protected Sub DeleteSelectedProducts_Click(sender As Object, e As EventArgs) _
    Handles DeleteSelectedProducts.Click
    
    ' Create a List to hold the ProductID values to delete
    Dim productIDsToDelete As New System.Collections.Generic.List(Of Integer)
    ' Iterate through the Products.Rows property
    For Each row As GridViewRow In Products.Rows
        ' Access the CheckBox
        Dim cb As CheckBox = CType(row.FindControl("ProductSelector"), CheckBox)
        If cb IsNot Nothing AndAlso cb.Checked Then
            ' Save the ProductID value for deletion
            ' First, get the ProductID for the selected row
            Dim productID As Integer = _
                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)
        End If
    Next
    ' Call the DeleteProductsWithTransaction method and show the Label 
    ' if at least one row was deleted...
    If productIDsToDelete.Count > 0 Then
        Dim productAPI As New ProductsBLL()
        productAPI.DeleteProductsWithTransaction(productIDsToDelete)
        DeleteResults.Visible = True
        ' Rebind the data to the GridView
        Products.DataBind()
    End If
End Sub

更新后的代码会创建一个List(Of T)类型的IntegerproductIDsToDelete),并用需要删除的ProductID值填充它。 循环 For Each 后,如果选择了至少一个产品,则调用 ProductsBLL 类的 DeleteProductsWithTransaction 方法,并传递此列表。 DeleteResults 标签也会显示,数据将重新绑定到 GridView(以便新删除的记录不再作为网格中的行显示)。

图 4 显示了在选择了一些行进行删除之后的 GridView。 图 5 显示单击“删除所选产品”按钮后立即显示的屏幕。 请注意,在图 5 中, ProductID 已删除记录的值显示在 GridView 下的 Label 中,这些行不再位于 GridView 中。

所选产品将被删除

图 4:将删除所选产品(单击以查看全尺寸图像

已删除的产品 ProductID 值列在 GridView 下

图 5:已删除的产品 ProductID 值列在 GridView 下面(单击以查看全尺寸图像

注释

若要测试 DeleteProductsWithTransaction 方法的原子性,请手动在表中为某个产品 Order Details 添加一个条目,然后尝试删除该产品(以及其他产品)。 当尝试删除与订单关联的单个产品时,将会收到外键约束冲突错误。但请注意,其他所选产品的删除将被回滚。

概要

创建批量删除接口涉及添加 GridView,其中包含复选框的一列和一个按钮 Web 控件,单击该控件后,将以单一原子操作方式删除所有选定的行。 在本教程中,我们构建了这样的接口,方法是将前面两个教程中完成的工作拼凑在一起, 即在事务中添加一个 GridView 复选框列包装数据库修改。 在第一个教程中,我们创建了一个 GridView,其中包含一列复选框,在后者中,我们在 BLL 中实现了一个方法,在传递List(Of T)ProductID值时,删除了它们在事务范围内的所有内容。

在下一教程中,我们将创建用于执行批处理插入的接口。

快乐编程!

关于作者

斯科特·米切尔,七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 《Sams Teach Yourself ASP.NET 2.0 in 24 Hours》。 可以通过 mitchell@4GuysFromRolla.com 联系到他。

特别致谢

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是希尔顿·吉塞诺和特蕾莎·墨菲。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请给我写信。mitchell@4GuysFromRolla.com