批量插入 (C#)
了解如何在单个操作中插入多个数据库记录。 在用户界面层中,我们扩展 GridView 以允许用户输入多个新记录。 在数据访问层中,我们在事务中包装多个插入操作,以确保所有插入都成功或回滚所有插入。
简介
在 Batch Updateing 教程中,我们介绍了如何自定义 GridView 控件,以呈现可编辑多个记录的接口。 访问页面的用户可以进行一系列更改,然后通过单击一个按钮执行批量更新。 对于用户通常一次性更新许多记录的情况,与 插入、更新和删除数据概述 教程中首次探索的默认每行编辑功能相比,此类界面可以保存无数次单击和键盘到鼠标上下文切换。
添加记录时也可以应用此概念。 试想一下,在 Northwind Traders,我们通常从包含特定类别许多产品的供应商处收到货物。 例如,我们可能会收到来自东京贸易商的六种不同茶和咖啡产品的发货。 如果用户通过 DetailsView 控件一次输入六个产品,他们将不得不反复选择许多相同的值:他们将需要选择相同的类别 (饮料) ,同一供应商 (东京贸易) ,相同的停产价值 (False) ,订单价值 (0) 相同的单位。 这种重复的数据输入不仅耗时,而且容易出错。
通过一点点工作,我们可以创建一个批处理插入界面,使用户能够选择供应商和类别一次,输入一系列产品名称和单价,然后单击按钮将新产品添加到数据库 (请参阅图 1) 。 添加每个产品时,会 ProductName
为其 和数据 UnitPrice
字段分配在 TextBoxes 中输入的值,而其 CategoryID
和 SupplierID
值则分配来自窗体顶部的 DropDownLists 的值。 Discontinued
和 UnitsOnOrder
值分别设置为 和 0 的false
硬编码值。
图 1:批量插入接口 (单击以查看全尺寸图像)
在本教程中,我们将创建一个实现批量插入接口的页面,如图 1 所示。 与前面的两个教程一样,我们将在事务范围内包装插入内容,以确保原子性。 让我们开始吧!
步骤 1:创建显示界面
本教程将包含一个页面,该页面分为两个区域:显示区域和插入区域。 我们将在此步骤中创建的显示界面显示 GridView 中的产品,并包含标题为“处理产品发货”的按钮。 单击此按钮时,显示界面将替换为插入界面,如图 1 所示。 单击“从发货添加产品”或“取消”按钮后,显示界面将返回。 我们将在步骤 2 中创建插入接口。
创建具有两个接口(一次只显示其中一个接口)的页面时,每个接口通常放置在 Panel Web 控件中,该控件用作其他控件的容器。 因此,我们的页面将具有两个面板控件,每个界面各有一个。
首先打开 文件夹中的页面BatchInsert.aspx
BatchData
,将“面板”从“工具箱”拖到Designer (请参阅图 2) 。 将 Panel 属性 ID
设置为 DisplayInterface
。 将面板添加到Designer时,其 Height
和 Width
属性分别设置为 50px 和 125px。 从属性窗口中清除这些属性值。
图 2:将面板从工具箱拖到Designer (单击以查看全尺寸图像)
接下来,将 Button 和 GridView 控件拖动到面板中。 将 Button 的 ID
属性设置为 ProcessShipment
,将其 Text
属性设置为 “处理产品发货”。 将 GridView 的 ID
属性设置为 ProductsGrid
,并从其智能标记中将其绑定到名为 ProductsDataSource
的新 ObjectDataSource。 配置 ObjectDataSource 以从 ProductsBLL
类 s GetProducts
方法拉取其数据。 由于此 GridView 仅用于显示数据,因此请将“更新”、“插入”和“删除”选项卡中的下拉列表设置为“无”) (。 单击“完成”以完成“配置数据源”向导。
图 3:显示从 ProductsBLL
类方法 GetProducts
返回的数据 (单击以查看全尺寸图像)
图 4:将“更新”、“插入”和“删除”选项卡中的 Drop-Down Lists 设置为“无 (”) (单击以查看全尺寸图像)
完成 ObjectDataSource 向导后,Visual Studio 将为产品数据字段添加 BoundFields 和 CheckBoxField。 删除除 、、CategoryName
、 SupplierName
UnitPrice
和 Discontinued
字段的所有ProductName
字段。 随意进行任何美观自定义。 我决定将 UnitPrice
字段的格式设置为货币值,对字段重新排序,并重命名了几个字段 HeaderText
值。 此外,通过选中 GridView 智能标记中的“启用分页”和“启用排序”复选框,将 GridView 配置为包含分页和排序支持。
添加 Panel、Button、GridView 和 ObjectDataSource 控件并自定义 GridView 字段后,页面声明性标记应如下所示:
<asp:Panel ID="DisplayInterface" runat="server">
<p>
<asp:Button ID="ProcessShipment" runat="server"
Text="Process Product Shipment" />
</p>
<asp:GridView ID="ProductsGrid" runat="server" AllowPaging="True"
AllowSorting="True" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
HeaderText="Price" HtmlEncode="False"
SortExpression="UnitPrice">
<ItemStyle HorizontalAlign="Right" />
</asp:BoundField>
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued">
<ItemStyle HorizontalAlign="Center" />
</asp:CheckBoxField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
</asp:Panel>
请注意,Button 和 GridView 的标记显示在开始和结束 <asp:Panel>
标记中。 由于这些控件位于 Panel 中 DisplayInterface
,因此只需将 Panel 的 Visible
属性设置为 false
即可隐藏它们。 步骤 3 查看以编程方式更改 Panel 属性 Visible
,以响应按钮单击以显示一个界面,同时隐藏另一个界面。
花点时间通过浏览器查看进度。 如图 5 所示,应在 GridView 上方看到一个“处理产品发货”按钮,该按钮一次列出 10 个产品。
图 5:GridView Lists产品和产品/服务排序和分页功能 (单击以查看全尺寸图像)
步骤 2:创建插入接口
完成显示界面后,我们即可创建插入界面。 在本教程中,让我们创建一个插入界面,提示输入单个供应商和类别值,然后允许用户输入最多五个产品名称和单价值。 使用此界面,用户可以添加一到五个新产品,这些产品具有相同的类别和供应商,但具有唯一的产品名称和价格。
首先,将“面板”从“工具箱”拖到Designer,将其放置在现有DisplayInterface
面板下方。 ID
将此新添加的 Panel 的 属性设置为 InsertingInterface
,并将其Visible
属性设置为 false
。 我们将在步骤 3 中添加将 Panel 属性Visible
设置为 InsertingInterface
true
的代码。 此外,请清除 Panel 和Height
Width
属性值。
接下来,我们需要创建如图 1 所示的插入接口。 可以通过各种 HTML 技术创建此接口,但我们将使用一个相当简单的方法:一个包含四列、七行的表。
注意
输入 HTML <table>
元素的标记时,我更喜欢使用“源”视图。 虽然 Visual Studio 确实具有通过Designer添加<table>
元素的工具,但Designer似乎太愿意将未分配style
的设置注入标记中。 创建<table>
标记后,通常会返回到Designer添加 Web 控件并设置其属性。 创建具有预先确定的列和行的表时,我更喜欢使用静态 HTML 而不是 表 Web 控件 ,因为放置在表 Web 控件中的任何 Web 控件只能使用 模式 FindControl("controlID")
访问。 但是,我确实将表 Web 控件用于动态大小的表, (行或列基于某些数据库或用户指定的条件) 的表,因为表 Web 控件可以通过编程方式构造。
在面板的InsertingInterface
标记中<asp:Panel>
输入以下标记:
<table class="DataWebControlStyle" cellspacing="0">
<tr class="BatchInsertHeaderRow">
<td class="BatchInsertLabel">Supplier:</td>
<td></td>
<td class="BatchInsertLabel">Category:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertAlternatingRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertAlternatingRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertRow">
<td class="BatchInsertLabel">Product:</td>
<td></td>
<td class="BatchInsertLabel">Price:</td>
<td></td>
</tr>
<tr class="BatchInsertFooterRow">
<td colspan="4">
</td>
</tr>
</table>
此 <table>
标记尚不包括任何 Web 控件,我们将暂时添加这些控件。 请注意,每个<tr>
元素都包含一个特定的 CSS 类设置:BatchInsertHeaderRow
对于供应商和类别 DropDownLists 将转到的标题行;BatchInsertFooterRow
对于从发货添加产品和取消按钮将转到的页脚行;以及将包含产品和单价 TextBox 控件的行的交替BatchInsertRow
BatchInsertAlternatingRow
和值。 我在 文件中创建了相应的 CSS 类 Styles.css
,使插入接口的外观类似于我们在这些教程中使用的 GridView 和 DetailsView 控件。 这些 CSS 类如下所示。
/*** Styles for ~/BatchData/BatchInsert.aspx tutorial ***/
.BatchInsertLabel
{
font-weight: bold;
text-align: right;
}
.BatchInsertHeaderRow td
{
color: White;
background-color: #900;
padding: 11px;
}
.BatchInsertFooterRow td
{
text-align: center;
padding-top: 5px;
}
.BatchInsertRow
{
}
.BatchInsertAlternatingRow
{
background-color: #fcc;
}
输入此标记后,返回到“设计”视图。 如图 6 所示,这<table>
应显示为Designer中由四列、七行构成的表。
图 6:插入接口由四列、Seven-Row 表 (单击以查看全尺寸图像)
现在,我们已准备好将 Web 控件添加到插入界面。 将两个 DropDownList 从工具箱拖到表中的相应单元格中,一个用于供应商,一个用于类别。
将供应商 DropDownList 属性 ID
设置为 Suppliers
,并将其绑定到名为 SuppliersDataSource
的新 ObjectDataSource。 配置新的 ObjectDataSource 以从 SuppliersBLL
类 s GetSuppliers
方法检索其数据,并将“更新”选项卡下拉列表设置为“无”) (。 单击“完成”以完成向导。
图 7:将 ObjectDataSource 配置为使用 SuppliersBLL
类 s GetSuppliers
方法 (单击以查看全尺寸图像)
Suppliers
让 DropDownList 显示CompanyName
数据字段,并将SupplierID
数据字段用作其ListItem
值。
图 8:显示 CompanyName
数据字段并用作 SupplierID
值 (单击以查看全尺寸图像)
将第二个 DropDownList Categories
命名为 ,并将其绑定到名为 CategoriesDataSource
的新 ObjectDataSource。 将 CategoriesDataSource
ObjectDataSource 配置为使用 CategoriesBLL
类 s GetCategories
方法;将“更新”和“删除”选项卡中的下拉列表设置为 (“无”) 并单击“完成”完成向导。 最后,让 DropDownList 显示 CategoryName
数据字段,并使用 CategoryID
作为值。
添加这两个 DropDownList 并将其绑定到适当配置的 ObjectDataSources 后,屏幕应类似于图 9。
图 9:标题行现在包含 Suppliers
和 Categories
DropDownLists (单击以查看全尺寸图像)
我们现在需要创建 TextBox 来收集每个新产品的名称和价格。 将一个 TextBox 控件从“工具箱”拖到五个产品名称和价格行的Designer上。 将 ID
TextBox 的属性设置为 ProductName1
、、UnitPrice1
ProductName2
、UnitPrice2
ProductName3
、UnitPrice3
、 等。
在每个单价 TextBox 之后添加 CompareValidator,并将 ControlToValidate
属性设置为相应的 ID
。 此外,将 Operator
属性设置为 GreaterThanEqual
, ValueToCompare
将 设置为 0,将 Type
设置为 Currency
。 这些设置指示 CompareValidator 确保输入的价格是大于或等于零的有效货币值。 将 Text
属性设置为 *,并将 ErrorMessage
设置为 价格必须大于或等于零。 此外,请省略任何货币符号。
注意
插入接口不包含任何 RequiredFieldValidator 控件,即使数据库表中的 ProductName
Products
字段不允许 NULL
值。 这是因为我们希望允许用户输入最多五个产品。 例如,如果用户提供前三行的产品名称和单价,将最后两行留空,我们只需向系统添加三个新产品。 但是,由于 ProductName
是必需的,因此我们需要以编程方式检查,以确保在输入单价时提供相应的产品名称值。 我们将在步骤 4 中解决此检查。
验证用户的输入时,如果值包含货币符号,则 CompareValidator 将报告无效数据。 在每个单价 TextBox 的前面添加 $,作为指示用户在输入价格时省略货币符号的视觉提示。
最后,在 Panel 中添加 ValidationSummary 控件 InsertingInterface
,将其 ShowMessageBox
属性设置为 true
,将其 ShowSummary
属性设置为 false
。 使用这些设置时,如果用户输入无效的单价值,则会在有问题的 TextBox 控件旁边显示星号,ValidationSummary 将显示一个客户端消息框,其中显示了我们之前指定的错误消息。
此时,屏幕应类似于图 10。
图 10:插入界面现在包括产品名称和价格的文本框 (单击以查看全尺寸图像)
接下来,我们需要将“从发货添加产品”和“取消”按钮添加到页脚行。 将两个按钮控件从“工具箱”拖到插入界面的页脚中,分别将 Buttons ID
属性设置为 AddProducts
和 CancelButton
,并将 Text
属性设置为 “从发货添加产品” 和 “取消” 。 此外,将 CancelButton
控件 的 CausesValidation
属性设置为 false
。
最后,我们需要添加一个标签 Web 控件,该控件将显示两个接口的状态消息。 例如,当用户成功添加新的一批产品时,我们希望返回到显示界面并显示确认消息。 但是,如果用户提供了新产品的价格,但未显示产品名称,我们需要显示一条警告消息, ProductName
因为字段是必需的。 由于我们需要为两个接口显示此消息,因此请将其放置在面板外部的页面顶部。
将“标签 Web”控件从“工具箱”拖到Designer页面顶部。 将 属性设置为 StatusLabel
,清除 Text
属性,并将 和 EnableViewState
属性设置为 Visible
false
。ID
正如我们在前面的教程中看到的,将 属性设置为 EnableViewState
false
允许我们以编程方式更改 Label 属性值,并在后续回发时将它们自动还原回到默认值。 这简化了显示状态消息的代码,以响应在后续回发中消失的某些用户操作。 最后,将 StatusLabel
控件 属性 CssClass
设置为 Warning,这是中 Styles.css
定义的 CSS 类的名称,该类以大斜体、粗体、红色字体显示文本。
图 11 显示了添加和配置 Label 后 Visual Studio Designer。
图 11:将 StatusLabel
控件置于两个面板控件上方 (单击以查看全尺寸图像)
步骤 3:在显示接口和插入接口之间切换
此时,我们已经完成了显示和插入接口的标记,但仍剩下两个任务:
- 在显示接口和插入接口之间切换
- 将发货中的产品添加到数据库
目前,显示界面可见,但插入界面处于隐藏状态。 这是因为 Panel DisplayInterface
的 属性设置为 true
(默认值) ,而 InsertingInterface
Panel 的 Visible
属性设置为 false
Visible
。 若要在两个接口之间切换,只需切换每个控件的 Visible
属性值。
我们希望在单击“处理产品发货”按钮时,从显示界面移动到插入界面。 因此,请为此 Button 事件创建包含以下代码的 Click
事件处理程序:
protected void ProcessShipment_Click(object sender, EventArgs e)
{
DisplayInterface.Visible = false;
InsertingInterface.Visible = true;
}
此代码只是隐藏面板 DisplayInterface
并显示 InsertingInterface
面板。
接下来,在插入界面中为“从发货添加产品”和“取消按钮”控件创建事件处理程序。 单击其中任一按钮时,需要还原回到显示界面。 为两个 Button 控件创建 Click
事件处理程序,以便它们调用 ReturnToDisplayInterface
,我们将暂时添加一个方法。 除了隐藏 InsertingInterface
面板并显示 DisplayInterface
面板外 ReturnToDisplayInterface
,该方法还需要将 Web 控件返回到其预编辑状态。 这涉及到将 DropDownLists SelectedIndex
属性设置为 0 并清除 Text
TextBox 控件的属性。
注意
请考虑在返回到显示界面之前,如果未将控件返回到其预编辑状态,会发生什么情况。 用户可以单击“处理产品发货”按钮,输入发货中的产品,然后单击“从发货添加产品”。 这将添加产品并将用户返回到显示界面。 此时,用户可能需要添加另一批货物。 单击“处理产品发货”按钮后,它们将返回到插入界面,但 DropDownList 选择和 TextBox 值仍将填充其以前的值。
protected void AddProducts_Click(object sender, EventArgs e)
{
// TODO: Save the products
// Revert to the display interface
ReturnToDisplayInterface();
}
protected void CancelButton_Click(object sender, EventArgs e)
{
// Revert to the display interface
ReturnToDisplayInterface();
}
const int firstControlID = 1;
const int lastControlID = 5;
private void ReturnToDisplayInterface()
{
// Reset the control values in the inserting interface
Suppliers.SelectedIndex = 0;
Categories.SelectedIndex = 0;
for (int i = firstControlID; i <= lastControlID; i++)
{
((TextBox)InsertingInterface.FindControl("ProductName" + i.ToString())).Text =
string.Empty;
((TextBox)InsertingInterface.FindControl("UnitPrice" + i.ToString())).Text =
string.Empty;
}
DisplayInterface.Visible = true;
InsertingInterface.Visible = false;
}
这两个 Click
事件处理程序都只是调用 ReturnToDisplayInterface
方法,不过我们将返回到步骤 4 中的“从发货 Click
添加产品”事件处理程序,并添加代码以保存产品。 ReturnToDisplayInterface
首先将 Suppliers
和 Categories
DropDownLists 返回到其第一个选项。 这两个常量firstControlID
和lastControlID
标记开始和结束控件索引值,用于命名插入接口中的产品名称和单价 TextBoxs,并在将 TextBox 控件的属性设置Text
回空字符串的for
循环边界中使用。 最后,重置 Panels Visible
属性,以便隐藏插入接口并显示显示界面。
花点时间在浏览器中测试此页面。 首次访问页面时,应会看到显示界面,如图 5 所示。 单击“处理产品发货”按钮。 页面将回发,现在应会看到插入界面,如图 12 所示。 单击“从发货添加产品”或“取消”按钮将返回到显示界面。
注意
查看插入界面时,请花点时间测试单价 TextBox 上的 CompareValidator。 单击“从发货添加产品”按钮时,应看到客户端消息框警告,该按钮的货币值无效或价格值小于零。
图 12:单击“处理产品发货”按钮后显示插入界面 (单击以查看全尺寸图像)
步骤 4:添加产品
本教程剩下的就是在“从发货添加产品”按钮事件处理程序 Click
中将产品保存到数据库。 为此,可以创建 ProductsDataTable
并为提供的每个产品名称添加 实例 ProductsRow
。 添加这些 ProductsRow
后,我们将调用ProductsBLL
传入 ProductsDataTable
的类 方法UpdateWithTransaction
。 UpdateWithTransaction
回想一下,在事务中包装数据库修改教程中创建的 方法将 传递给 ProductsDataTable
ProductsTableAdapter
s UpdateWithTransaction
方法。 从该处,将启动 ADO.NET 事务,并且 TableAdapter 针对 DataTable 中添加的每个ProductsRow
操作向数据库发出INSERT
语句。 假设添加所有产品且未出错,则会提交事务,否则将回滚该事务。
“从发货按钮添加产品”事件处理程序的代码 Click
还需要执行一些错误检查。 由于插入界面中未使用 RequiredFieldValidator,因此用户可以输入产品的价格,同时省略其名称。 由于产品名称是必需的,因此,如果出现这种情况,我们需要提醒用户,而不是继续插入。 完整的 Click
事件处理程序代码如下:
protected void AddProducts_Click(object sender, EventArgs e)
{
// Make sure that the UnitPrice CompareValidators report valid data...
if (!Page.IsValid)
return;
// Add new ProductsRows to a ProductsDataTable...
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
for (int i = firstControlID; i <= lastControlID; i++)
{
// Read in the values for the product name and unit price
string productName = ((TextBox)InsertingInterface.FindControl
("ProductName" + i.ToString())).Text.Trim();
string unitPrice = ((TextBox)InsertingInterface.FindControl
("UnitPrice" + i.ToString())).Text.Trim();
// Ensure that if unitPrice has a value, so does productName
if (unitPrice.Length > 0 && productName.Length == 0)
{
// Display a warning and exit this event handler
StatusLabel.Text = "If you provide a unit price you must also " +
"include the name of the product.";
StatusLabel.Visible = true;
return;
}
// Only add the product if a product name value is provided
if (productName.Length > 0)
{
// Add a new ProductsRow to the ProductsDataTable
Northwind.ProductsRow newProduct = products.NewProductsRow();
// Assign the values from the web page
newProduct.ProductName = productName;
newProduct.SupplierID = Convert.ToInt32(Suppliers.SelectedValue);
newProduct.CategoryID = Convert.ToInt32(Categories.SelectedValue);
if (unitPrice.Length > 0)
newProduct.UnitPrice = Convert.ToDecimal(unitPrice);
// Add any "default" values
newProduct.Discontinued = false;
newProduct.UnitsOnOrder = 0;
products.AddProductsRow(newProduct);
}
}
// If we reach here, see if there were any products added
if (products.Count > 0)
{
// Add the new products to the database using a transaction
ProductsBLL productsAPI = new ProductsBLL();
productsAPI.UpdateWithTransaction(products);
// Rebind the data to the grid so that the products just added are displayed
ProductsGrid.DataBind();
// Display a confirmation (don't use the Warning CSS class, though)
StatusLabel.CssClass = string.Empty;
StatusLabel.Text = string.Format(
"{0} products from supplier {1} have been added and filed under " +
"category {2}.", products.Count, Suppliers.SelectedItem.Text,
Categories.SelectedItem.Text);
StatusLabel.Visible = true;
// Revert to the display interface
ReturnToDisplayInterface();
}
else
{
// No products supplied!
StatusLabel.Text = "No products were added. Please enter the product " +
"names and unit prices in the textboxes.";
StatusLabel.Visible = true;
}
}
事件处理程序首先确保 Page.IsValid
属性返回值 true
。 如果它返回 false
,则表示一个或多个 CompareValidator 报告无效数据;在这种情况下,我们不想尝试插入输入的产品,或者在尝试将用户输入的单价值 ProductsRow
分配给 属性 UnitPrice
时,最终会出现异常。
接下来, ProductsDataTable
() products
创建新实例。 循环 for
用于循环访问产品名称和单价 TextBox,并将 Text
属性读入局部变量 productName
和 unitPrice
。 如果用户为单价输入了值,但未输入相应的产品名称,则 StatusLabel
将显示消息。如果提供单价,则还必须包含产品名称,并退出事件处理程序。
如果已提供产品名称,则使用 ProductsDataTable
s NewProductsRow
方法创建一个新ProductsRow
实例。 此新 ProductsRow
实例 的 ProductName
属性设置为当前产品名称 TextBox, SupplierID
而 和 CategoryID
属性则 SelectedValue
分配给插入接口标头中的 DropDownLists 的属性。 如果用户输入了产品价格的值,则会将其分配给 ProductsRow
实例的 属性 UnitPrice
;否则,该属性将保持未分配状态,这将导致 NULL
数据库中 的 UnitPrice
值。 最后,将 Discontinued
和 UnitsOnOrder
属性分别分配给硬编码值 false
和 0。
将属性分配给 实例后,ProductsRow
该属性将添加到 。ProductsDataTable
循环完成后for
,我们检查是否已添加任何产品。 毕竟,在输入任何产品名称或价格之前,用户可能已单击“从发货添加产品”。 如果 中 ProductsDataTable
至少有一个积,则 ProductsBLL
调用 类方法 UpdateWithTransaction
。 接下来,数据将反弹到 ProductsGrid
GridView,以便新添加的产品将显示在显示界面中。 将 StatusLabel
更新 以显示确认消息,并 ReturnToDisplayInterface
调用 ,隐藏插入接口并显示显示接口。
如果未输入任何产品,则仍显示插入界面,但消息“未添加产品”。 请在显示的文本框中输入产品名称和单价。
图 13、14 和 15 显示了操作中的插入和显示接口。 在图 13 中,用户输入的单价值没有相应的产品名称。 图 14 显示了成功添加三个新产品后的显示界面,而图 15 显示了 GridView 中新增的两个产品 (第三个产品位于上一页) 。
图 13:输入单价时需要产品名称 (单击以查看全尺寸图像)
图 14:已为供应商 Mayumi s 添加了三个新蔬菜 (单击以查看全尺寸图像)
图 15:可在 GridView 的最后一页中找到新产品 (单击以查看全尺寸图像)
注意
本教程中使用的批量插入逻辑将插入包装在事务范围内。 若要验证这一点,请有意引入数据库级错误。 例如,与其将新 ProductsRow
实例属性 CategoryID
分配给 DropDownList 中的 Categories
选定值,不如将其分配给 值 i * 5
。 下面是 i
循环索引器,其值范围为 1 到 5。 因此,在批量插入两个或更多产品时,第一个CategoryID
产品的有效值 (5) ,但后续产品的值与CategoryID
表中的值Categories
不匹配CategoryID
。 净效果是,虽然第一个 INSERT
将成功,但后续失败并违反外键约束。 由于批处理插入是原子的,因此将回滚第一个 INSERT
,使数据库在批处理插入过程开始前恢复到其状态。
总结
在此教程和前两个教程中,我们创建了允许更新、删除和插入数据批处理的接口,所有这些接口都使用了我们在事务教程中 包装数据库修改 中添加到数据访问层的事务支持。 在某些情况下,此类批处理用户界面通过减少单击、回发和键盘到鼠标上下文切换的数量,极大地提高了最终用户的效率,同时保持基础数据的完整性。
本教程介绍了如何使用批处理数据。 下一组教程探讨了各种高级数据访问层方案,包括使用 TableAdapter 方法中的存储过程、在 DAL 中配置连接和命令级设置、加密连接字符串等!
编程快乐!
关于作者
斯科特·米切尔是七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直在使用 Microsoft Web 技术。 Scott 担任独立顾问、培训师和作家。 他的最新一本书是 山姆斯在 24 小时内 ASP.NET 2.0。 可以在 上mitchell@4GuysFromRolla.com联系他,也可以通过他的博客(可在 中找到http://ScottOnWriting.NET)。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是希尔顿·吉塞诺和 S ren Jacob Lauritsen。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处mitchell@4GuysFromRolla.com放置一行。