作者 :斯科特·米切尔
了解如何在单个作中删除多个数据库记录。 在 UI 层中,我们基于之前教程中创建的增强型 GridView 进行构建。 在数据访问层中,我们会在事务中将多个删除操作封装起来,以确保所有删除都成功或确保回滚所有删除。
介绍
前面的教程探讨了如何使用完全可编辑的 GridView 创建批处理编辑界面。 在用户经常同时编辑许多记录的情况下,批量编辑界面需要更少的回传和减少切换上下文从键盘到鼠标,从而提高用户效率。 此方法同样适用于用户一次性删除许多记录的页面。
使用联机电子邮件客户端的任何人都可以熟悉最常见的批量删除接口之一:网格中每一行中的复选框,其中包含相应的“删除所有选中项目”按钮(请参阅图 1)。 本教程相当简短,因为我们在之前的教程中已完成所有艰苦的工作,包括创建网页界面以及实现一种将一系列记录作为一个原子操作删除的方法。 在“添加 GridView 复选框列”教程中,我们创建了一个包含复选框列的 GridView,并在“通过事务包装数据库修改”教程中,在业务逻辑层(BLL)中创建了一个方法,该方法使用事务来删除List<T>
ProductID
的值。 在本教程中,我们将基于前面的体验构建和合并,以创建一个工作批处理删除示例。
图 1:每行都包含一个复选框(单击以查看全尺寸图像)
步骤 1:创建批处理删除接口
由于我们已在 “添加复选框的 GridView 列 ”教程中创建批处理删除接口,因此我们只需将其 BatchDelete.aspx
复制到而不是从头开始创建它。 首先打开 BatchDelete.aspx
文件夹中的页面 BatchData
和 CheckBoxField.aspx
文件夹中的页面 EnhancedGridView
。 在CheckBoxField.aspx
页面上访问“源”视图,并复制位于<asp:Content>
标签之间的标记,如图 2 所示。
图 2:将声明性标记 CheckBoxField.aspx
复制到剪贴板(单击以查看全尺寸图像)
接下来,进入BatchDelete.aspx
的“源视图”,然后将剪贴板中的内容粘贴到<asp:Content>
标签中。 此外,将代码从后台代码类中的CheckBoxField.aspx.cs
复制并粘贴到后台代码类中的BatchDelete.aspx.cs
(即DeleteSelectedProducts
按钮的Click
事件处理程序、ToggleCheckState
方法,以及Click
和CheckAll
按钮的UncheckAll
事件处理程序)。 复制此内容后, 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
。 您应该会看到一个 GridView,其中列出前 10 个产品。每行显示产品名称、类别和价格,并附有一个复选框。 应有三个按钮:“全选”、“取消全选”和“删除所选产品”。 单击“全部选中”按钮将选中所有复选框,“全部取消选中”将清除所有复选框。 单击“删除所选产品”会显示一条消息,其中列出了 ProductID
所选产品的值,但实际上不会删除产品。
图 3:已将接口 CheckBoxField.aspx
移动到 BatchDeleting.aspx
(单击以查看全尺寸图像)
第 2 步:使用事务删除已检查的商品
当批量删除接口成功复制到 BatchDeleting.aspx
后,剩下的任务仅仅是更新代码,以便“删除所选产品”按钮使用 ProductsBLL
类中的 DeleteProductsWithTransaction
方法删除已检查的产品。 此方法在事务教程中的包装数据库修改中添加,接受其输入List<T>
ProductID
值并删除事务范围内的每个对应ProductID
项。
DeleteSelectedProducts
Button s Click
事件处理程序当前使用以下foreach
循环循环遍历每个 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
标签的Text
属性将被更新,以包含一条消息,指示该行已被选择用于删除。
上述代码实际上不会删除任何记录,因为对类ProductsBLL
调用方法Delete
被注释掉。如果应用此删除逻辑,代码将删除产品,但不在原子操作中进行。 也就是说,如果序列中的前几个删除成功,但后来的一个删除失败(可能是由于外键约束冲突),则会引发异常,但那些已经删除的产品将保持删除状态。
为了保证原子性,我们需要改用 ProductsBLL
类中的 DeleteProductsWithTransaction
方法。 由于此方法接受值列表 ProductID
,因此我们需要首先从网格编译此列表,然后将其作为参数传递。 我们首先创建一个 List<T>
类型的 int
实例。 在 foreach
循环中,我们需要将所选产品 ProductID
值添加到此 List<T>
。 循环后,必须将List<T>
传递给ProductsBLL
类的DeleteProductsWithTransaction
方法。 使用以下代码更新DeleteSelectedProducts
按钮的Click
事件处理程序:
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();
}
}
更新后的代码会创建一个List<T>
类型的int
(productIDsToDelete
),并用需要删除的ProductID
值填充它。 循环 foreach
后,如果选择了至少一个产品,则调用 ProductsBLL
类的 DeleteProductsWithTransaction
方法,并传递此列表。
DeleteResults
标签也会显示,数据将重新绑定到 GridView(以便新删除的记录不再作为网格中的行显示)。
图 4 显示了在选择了一些行进行删除之后的 GridView。 图 5 显示单击“删除所选产品”按钮后立即显示的屏幕。 请注意,在图 5 中, ProductID
已删除记录的值显示在 GridView 下的 Label 中,这些行不再位于 GridView 中。
图 4:将删除所选产品(单击以查看全尺寸图像)
图 5:已删除的产品 ProductID
值列在 GridView 下面(单击以查看全尺寸图像)
注释
若要测试 DeleteProductsWithTransaction
方法的原子性,请手动在表中为某个产品 Order Details
添加一个条目,然后尝试删除该产品(以及其他产品)。 当尝试删除与订单关联的单个产品时,将会收到外键约束冲突错误。但请注意,其他所选产品的删除将被回滚。
概要
创建批量删除接口涉及添加 GridView,其中包含复选框的一列和一个按钮 Web 控件,单击该控件后,将以单一原子操作方式删除所有选定的行。 在本教程中,我们构建了这样的接口,方法是将前面两个教程中完成的工作拼凑在一起, 即在事务中添加一个 GridView 复选框列 和 包装数据库修改。 在第一个教程中,我们创建了一个 GridView,其中包含一列复选框,在后者中,我们在 BLL 中实现了一个方法,在传递List<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