批量删除 (VB)

作者 :Scott Mitchell

下载 PDF

了解如何在单个操作中删除多个数据库记录。 在用户界面层中,我们基于前面教程中创建的增强型 GridView 构建。 在数据访问层中,我们在事务中包装多个删除操作,以确保所有删除都成功或回滚所有删除。

简介

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

使用过联机电子邮件客户端的任何人都已经熟悉最常见的批量删除界面之一:网格中每一行中的复选框以及相应的“删除所有选中的项目”按钮 (请参阅图 1) 。 本教程相当简短,因为我们已经完成了前面教程中关于创建基于 Web 的接口和作为单个原子操作删除一系列记录的方法的所有艰苦工作。 在添加复选框的 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 Button 的 Click 事件处理程序、ClickToggleCheckState方法和 和 Buttons 的事件处理程序 CheckAllUncheckAll) 。 复制此内容后, 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 列出 GridView 中的前十个产品,每一行都列出了产品的名称、类别和价格以及一个复选框。 应有三个按钮:“全部选中”、“全部取消选中”和“删除所选产品”。 单击“全部选中”按钮会选中所有复选框,而取消选中“全部”将清除所有复选框。 单击“删除所选产品”会显示一条消息, ProductID 其中列出了所选产品的值,但实际上不会删除产品。

CheckBoxField.aspx 中的接口已移动到BatchDeleting.aspx

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

步骤 2:使用事务删除已检查的产品

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

DeleteSelectedProducts Button 事件处理程序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 控件。 如果选中该行,则会从DataKeys集合中检索该行ProductID,并且 DeleteResults Label 属性Text将更新为包含一条消息,指示已选择要删除的行。

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

为了确保原子性,我们需要改用 ProductsBLL 类 s DeleteProductsWithTransaction 方法。 由于此方法接受值列表 ProductID ,因此我们需要首先从网格编译此列表,然后将其作为参数传递。 首先创建 类型IntegerList(Of T) 的 实例。 在 循环中 For Each ,我们需要将所选产品 ProductID 值添加到此 List(Of T)。 在 循环之后, List(Of T) 必须将此传递给 ProductsBLL 类 的 DeleteProductsWithTransaction 方法。 使用以下 DeleteSelectedProducts 代码更新 Button 事件处理程序 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)类型为 (productIDsToDelete) ,并使用要删除的值填充它ProductIDInteger 。 循环后 For Each ,如果至少选择了一个产品,则 ProductsBLL 调用 类 s DeleteProductsWithTransaction 方法并传递此列表。 DeleteResults还会显示“标签”,并将数据反弹到 GridView (,以便新删除的记录不再显示为网格) 中的行。

图 4 显示了选择要删除的行数后的 GridView。 图 5 显示了单击“删除所选产品”按钮后紧接的屏幕。 请注意,在图 5 中, ProductID 已删除记录的值显示在 GridView 下的标签中,这些行不再位于 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 技术。 Scott 担任独立顾问、培训师和作家。 他的最新一本书是 山姆斯在 24 小时内 ASP.NET 2.0。 可以在 上mitchell@4GuysFromRolla.com联系他,也可以通过他的博客(可在 中找到http://ScottOnWriting.NET)。

特别感谢

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