批量更新 (C#)
了解如何在单个操作中更新多个数据库记录。 在用户界面层中,我们生成一个 GridView,其中每一行都是可编辑的。 在数据访问层中,我们会在事务中包装多个更新操作,以确保所有更新成功或回滚所有更新。
简介
在 前面的教程中 ,我们了解了如何扩展数据访问层以添加对数据库事务的支持。 数据库事务保证一系列数据修改语句被视为一个原子操作,这可确保所有修改都将失败或全部成功。 随着这种低级别 DAL 功能的推出,我们准备将注意力转向创建批处理数据修改接口。
在本教程中,我们将生成一个 GridView,其中每一行都是可编辑的 (见图 1) 。 由于每一行都呈现在其编辑界面中,因此不需要“编辑”、“更新”和“取消”按钮列。 相反,页面上有两个“更新产品”按钮,单击这些按钮时,会枚举 GridView 行并更新数据库。
图 1:GridView 中的每一行都是可编辑 (单击以查看全尺寸图像)
让我们开始吧!
注意
在“执行批处理汇报教程中,我们使用 DataList 控件创建了批处理编辑界面。 本教程与上一教程的不同之处在于,它使用 GridView,批处理更新是在事务范围内执行的。 完成本教程后,建议返回到前面的教程,并将其更新为使用上一教程中添加的数据库事务相关功能。
检查使所有 GridView 行可编辑的步骤
如 插入、更新和删除数据概述 教程中所述,GridView 为按行编辑其基础数据提供内置支持。 在内部,GridView 通过其 EditIndex
属性记录可编辑的行。 当 GridView 绑定到其数据源时,它会检查每一行以查看行的索引是否等于 的值 EditIndex
。 如果是这样,则使用其编辑接口呈现该行的字段。 对于 BoundFields,编辑接口是一个 TextBox,其 Text
属性被分配为 BoundField 属性 DataField
指定的数据字段的值。 对于 TemplateFields, EditItemTemplate
使用 代替 ItemTemplate
。
回想一下,编辑工作流在用户单击行“编辑”按钮时启动。 这会导致回发,将 GridView 的 EditIndex
属性设置为单击的行索引,并将数据重新绑定到网格。 单击行“取消”按钮时,在回发时, EditIndex
将 设置为 值 -1
,然后再将数据重新绑定到网格。 由于 GridView 的行从零开始编制索引,将 设置为 EditIndex
-1
将具有在只读模式下显示 GridView 的效果。
属性 EditIndex
适用于每行编辑,但不适用于批量编辑。 若要使整个 GridView 可编辑,我们需要使用其编辑界面呈现每一行。 实现此目的的最简单方法是创建每个可编辑字段实现为 TemplateField 的位置,并在 中 ItemTemplate
定义其编辑接口。
在接下来的几个步骤中,我们将创建一个完全可编辑的 GridView。 在步骤 1 中,我们将首先创建 GridView 及其 ObjectDataSource,并将其 BoundFields 和 CheckBoxField 转换为 TemplateFields。 在步骤 2 和 3 中,我们将编辑接口从 TemplateFields EditItemTemplate
移动到其 ItemTemplate
。
步骤 1:显示产品信息
在担心创建行可编辑的 GridView 之前,让我们先显示产品信息。 BatchUpdate.aspx
打开 文件夹中的页面BatchData
,并将 GridView 从“工具箱”拖到Designer。 将 GridView ID
设置为 ProductsGrid
,并从其智能标记中选择将其绑定到名为 ProductsDataSource
的新 ObjectDataSource。 将 ObjectDataSource 配置为从 ProductsBLL
类方法 GetProducts
检索其数据。
图 2:将 ObjectDataSource 配置为使用 ProductsBLL
类 (单击以查看全尺寸图像)
图 3:使用 GetProducts
方法检索产品数据 (单击以查看全尺寸图像)
与 GridView 一样,ObjectDataSource 的修改功能设计为按行工作。 为了更新一组记录,我们需要在 ASP.NET 页代码隐藏类中编写一些代码,以便对数据进行批处理并将其传递给 BLL。 因此,请将 ObjectDataSource 的“更新”、“插入”和“删除”选项卡中的下拉列表设置为 (None) 。 单击“完成”以完成向导。
图 4:将“更新”、“插入”和“删除”选项卡中的 Drop-Down Lists 设置为“无 () (单击以查看全尺寸图像)
完成“配置数据源”向导后,ObjectDataSource 声明性标记应如下所示:
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
完成“配置数据源”向导还会导致 Visual Studio 为 GridView 中的产品数据字段创建 BoundFields 和 CheckBoxField。 在本教程中,我们仅允许用户查看和编辑产品名称、类别、价格和停产状态。 删除除 、、 和 字段之外ProductName
的所有字段,并将前三个字段的属性分别重命名HeaderText
为 Product、Category 和 Price。Discontinued
UnitPrice
CategoryName
最后,检查 GridView 智能标记中的“启用分页”和“启用排序”复选框。
此时,GridView 具有三个 BoundField (ProductName
、 CategoryName
和 UnitPrice
) ,以及一个 CheckBoxField (Discontinued
) 。 我们需要将这四个字段转换为 TemplateFields,然后将编辑界面从 TemplateField 移动到 EditItemTemplate
其 ItemTemplate
。
注意
我们在自定义 数据修改接口 教程中介绍了如何创建和自定义 TemplateFields。 我们将逐步完成将 BoundFields 和 CheckBoxField 转换为 TemplateFields 并在其 ItemTemplate
中定义其编辑接口的步骤,但如果遇到困难或需要复习,请随时重新参阅本前面的教程。
在 GridView 智能标记中,单击“编辑列”链接以打开“字段”对话框。 接下来,选择每个字段,然后单击“将此字段转换为 TemplateField”链接。
图 5:将现有边界字段和 CheckBoxField 转换为 TemplateField
现在,每个字段都是一个 TemplateField,我们准备将编辑界面从 EditItemTemplate
ItemTemplate
移动到 s。
步骤 2:创建ProductName
、UnitPrice
编辑接口和Discontinued
编辑接口
ProductName
创建 、 UnitPrice
和 Discontinued
编辑接口是此步骤的主题,并且非常简单,因为每个接口已在 TemplateField 中EditItemTemplate
定义。 CategoryName
创建编辑界面有点复杂,因为我们需要创建适用类别的 DropDownList。 此 CategoryName
编辑界面在步骤 3 中处理。
让我们从 TemplateField 开始 ProductName
。 单击 GridView 智能标记中的“编辑模板”链接,向下钻取到 ProductName
TemplateField 。EditItemTemplate
选择 TextBox,将其复制到剪贴板,然后将其粘贴到 ProductName
TemplateField 中 ItemTemplate
。 将 TextBox 属性 ID
更改为 ProductName
。
接下来,将 RequiredFieldValidator 添加到 , ItemTemplate
以确保用户为每个产品名称提供值。 将 ControlToValidate
属性设置为 ProductName,将 ErrorMessage
属性设置为 必须提供产品名称。 Text
将 属性设置为 *。 对 进行这些添加 ItemTemplate
后,屏幕应类似于图 6。
图 6:TemplateField ProductName
现在包括一个 TextBox 和一个 RequiredFieldValidator (单击以查看全尺寸图像)
UnitPrice
对于编辑界面,首先将 TextBox 从 EditItemTemplate
ItemTemplate
复制到 。 接下来,在 TextBox 前面放置 $,并将其 ID
属性设置为 UnitPrice,将其 Columns
属性设置为 8。
此外,将 CompareValidator 添加到 UnitPrice
, ItemTemplate
以确保用户输入的值是大于或等于 $0.00 的有效货币值。 将验证程序属性 ControlToValidate
设置为 UnitPrice,将其 ErrorMessage
属性设置为必须输入有效的货币值。 请省略任何货币符号。其 Text
属性为 *,其 Type
属性为 Currency
,其 Operator
属性为 GreaterThanEqual
,其 ValueToCompare
属性为 0 。
图 7:添加 CompareValidator 以确保输入的价格为非负货币值 (单击以查看全尺寸图像)
Discontinued
对于 TemplateField,可以使用 已在 中ItemTemplate
定义的 CheckBox。 只需将其 ID
设置为 Discontinued,将其 Enabled
属性设置为 true
。
步骤 3:创建CategoryName
编辑界面
TemplateField 中的CategoryName
EditItemTemplate
编辑界面包含显示数据字段值的 CategoryName
TextBox。 我们需要将其替换为列出可能类别的 DropDownList。
注意
自定义数据修改接口教程包含有关自定义模板以包含 DropDownList 而不是 TextBox 的更全面和完整的讨论。 虽然此处的步骤已完成,但它们会简洁地呈现。 若要更深入地了解如何创建和配置类别 DropDownList,请参阅 自定义数据修改接口 教程。
将 DropDownList 从工具箱拖到 CategoryName
TemplateField 上 ItemTemplate
,将其 ID
设置为 Categories
。 此时,我们通常会通过其智能标记定义 DropDownLists 的数据源,从而创建新的 ObjectDataSource。 但是,这会在 中添加 ItemTemplate
ObjectDataSource,这将导致为每个 GridView 行创建 ObjectDataSource 实例。 相反,让我们在 GridView 的 TemplateFields 外部创建 ObjectDataSource。 结束模板编辑,并将 ObjectDataSource 从工具箱拖到 ObjectDataSource 下的ProductsDataSource
Designer上。 将新的 ObjectDataSource CategoriesDataSource
命名为 ,并将其配置为使用 CategoriesBLL
类方法 GetCategories
。
图 8:将 ObjectDataSource 配置为使用 CategoriesBLL
类 (单击以查看全尺寸图像)
图 9:使用 GetCategories
方法检索类别数据 (单击以查看全尺寸图像)
由于此 ObjectDataSource 仅用于检索数据,因此请将“更新”和“删除”选项卡中的下拉列表设置为 (“无”) 。 单击“完成”以完成向导。
图 10:将“更新”和“删除”选项卡中的 Drop-Down Lists 设置为“ (无”) (单击查看全尺寸图像)
完成向导后, CategoriesDataSource
声明性标记应如下所示:
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
CategoriesDataSource
创建并配置 后,返回到 CategoryName
TemplateField,ItemTemplate
并从 DropDownList 的智能标记中单击“选择数据源”链接。 在“数据源配置”向导中,从第一个下拉列表中选择 CategoriesDataSource
选项,并选择 CategoryName
已用于显示 和 CategoryID
作为值。
图 11:将 DropDownList CategoriesDataSource
绑定到 (单击以查看全尺寸图像)
此时, Categories
DropDownList 会列出所有类别,但它尚未自动为绑定到 GridView 行的产品选择适当的类别。 为此,我们需要将 Categories
DropDownList 设置为 SelectedValue
product 值 CategoryID
。 单击 DropDownList 智能标记中的“编辑数据绑定”链接,并将 SelectedValue
属性与 CategoryID
数据字段相关联,如图 12 所示。
图 12:将 Product s CategoryID
值绑定到 DropDownList s SelectedValue
属性
最后一个问题仍然存在:如果产品未 CategoryID
指定值,则上的 SelectedValue
databinding 语句将导致异常。 这是因为 DropDownList 仅包含类别的项,并且不为数据库 NULL
值为 CategoryID
的产品提供选项。 若要解决此问题,请将 DropDownList 的 AppendDataBoundItems
属性设置为 true
,并将新项添加到 DropDownList,从声明性语法中省略该 Value
属性。 也就是说,请确保 Categories
DropDownList 声明性语法如下所示:
<asp:DropDownList ID="Categories" runat="server" AppendDataBoundItems="True"
DataSourceID="CategoriesDataSource" DataTextField="CategoryName"
DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'>
<asp:ListItem Value=">-- Select One --</asp:ListItem>
</asp:DropDownList>
请注意 -- Select One -- 将其Value
属性显式设置为空字符串的方式<asp:ListItem Value="">
。 请参阅 自定义数据修改接口 教程,更全面地讨论为什么需要此附加的 DropDownList 项来处理 NULL
这种情况,以及为什么将 属性分配给 Value
空字符串是必需的。
注意
此处存在一个潜在的性能和可伸缩性问题,值得一提。 由于每一行都有一个使用 CategoriesDataSource
作为其数据源的 DropDownList,CategoriesBLL
因此每次页面访问都会调用 n 次类方法GetCategories
,其中 n 是 GridView 中的行数。 这些 n 次 GetCategories
调用会导致对数据库的 n 个查询。 通过在每请求缓存中缓存返回的类别,或者通过使用 SQL 缓存依赖项或通过缓存层缓存返回的类别或基于非常短时间的过期时间,可以减轻对数据库的这种影响。
步骤 4:完成编辑界面
我们已对 GridView 模板进行了许多更改,但没有暂停查看进度。 花点时间通过浏览器查看进度。 如图 13 所示,每一行都使用包含 ItemTemplate
单元格编辑界面的 呈现。
图 13:每个 GridView 行都是可编辑的 (单击以查看全尺寸图像)
此时,我们应处理一些次要的格式设置问题。 首先,请注意该值 UnitPrice
包含四个小数点。 若要解决此问题,请返回到 UnitPrice
TemplateField, ItemTemplate
并从 TextBox 智能标记中单击“编辑数据绑定”链接。 接下来,指定 Text
属性应设置为数字格式。
图 14:将 Text
属性的格式设置为数字
其次,让我们在 Discontinued
(列中将复选框居中,而不是使其左对齐) 。 单击 GridView 智能标记中的“编辑列” Discontinued
,然后从左下角的字段列表中选择“TemplateField”。 向下 ItemStyle
钻取属性并将其 HorizontalAlign
设置为 Center,如图 15 所示。
图 15:将复选框居中Discontinued
接下来,将 ValidationSummary 控件添加到页面,并将其 ShowMessageBox
属性设置为 true
,将其 ShowSummary
属性设置为 false
。 此外,添加按钮 Web 控件,单击该控件将更新用户的更改。 具体而言,添加两个按钮 Web 控件,一个位于 GridView 上方,一个位于其下方,将这两个控件 Text
属性设置为“更新产品”。
由于 GridView 的编辑界面在其 TemplateFields ItemTemplate
中定义, EditItemTemplate
因此 是多余的,可能会被删除。
在进行上述格式更改、添加 Button 控件并删除不必要的 EditItemTemplate
之后,页面 声明性语法应如下所示:
<p>
<asp:Button ID="UpdateAllProducts1" runat="server" Text="Update Products" />
</p>
<p>
<asp:GridView ID="ProductsGrid" runat="server" AutoGenerateColumns="False"
DataKeyNames="ProductID" DataSourceID="ProductsDataSource"
AllowPaging="True" AllowSorting="True">
<Columns>
<asp:TemplateField HeaderText="Product" SortExpression="ProductName">
<ItemTemplate>
<asp:TextBox ID="ProductName" runat="server"
Text='<%# Bind("ProductName") %>'></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1"
ControlToValidate="ProductName"
ErrorMessage="You must provide the product's name."
runat="server">*</asp:RequiredFieldValidator>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Category"
SortExpression="CategoryName">
<ItemTemplate>
<asp:DropDownList ID="Categories" runat="server"
AppendDataBoundItems="True"
DataSourceID="CategoriesDataSource"
DataTextField="CategoryName"
DataValueField="CategoryID"
SelectedValue='<%# Bind("CategoryID") %>'>
<asp:ListItem>-- Select One --</asp:ListItem>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Price"
SortExpression="UnitPrice">
<ItemTemplate>
$<asp:TextBox ID="UnitPrice" runat="server" Columns="8"
Text='<%# Bind("UnitPrice", "{0:N}") %>'></asp:TextBox>
<asp:CompareValidator ID="CompareValidator1" runat="server"
ControlToValidate="UnitPrice"
ErrorMessage="You must enter a valid currency value.
Please omit any currency symbols."
Operator="GreaterThanEqual" Type="Currency"
ValueToCompare="0">*</asp:CompareValidator>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Discontinued" SortExpression="Discontinued">
<ItemTemplate>
<asp:CheckBox ID="Discontinued" runat="server"
Checked='<%# Bind("Discontinued") %>' />
</ItemTemplate>
<ItemStyle HorizontalAlign="Center" />
</asp:TemplateField>
</Columns>
</asp:GridView>
</p>
<p>
<asp:Button ID="UpdateAllProducts2" runat="server" Text="Update Products" />
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetProducts" TypeName="ProductsBLL">
</asp:ObjectDataSource>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>
<asp:ValidationSummary ID="ValidationSummary1" runat="server"
ShowMessageBox="True" ShowSummary="False" />
</p>
图 16 显示了在添加按钮 Web 控件并更改格式设置后通过浏览器查看的此页面。
图 16:页面现在包含两个更新产品按钮 (单击以查看全尺寸图像)
步骤 5:更新产品
当用户访问此页面时,他们将进行修改,然后单击两个“更新产品”按钮之一。 此时,我们需要以某种方式将每一行的用户输入的值保存到实例 ProductsDataTable
中,然后将其传递给 BLL 方法,该方法随后会将该 ProductsDataTable
实例传递给 DAL s UpdateWithTransaction
方法。 UpdateWithTransaction
我们在上一教程中创建的 方法可确保将批更改更新为原子操作。
在 中创建BatchUpdate.aspx.cs
名为 BatchUpdate
的方法,并添加以下代码:
private void BatchUpdate()
{
// Enumerate the GridView's Rows collection and create a ProductRow
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = productsAPI.GetProducts();
foreach (GridViewRow gvRow in ProductsGrid.Rows)
{
// Find the ProductsRow instance in products that maps to gvRow
int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
Northwind.ProductsRow product = products.FindByProductID(productID);
if (product != null)
{
// Programmatically access the form field elements in the
// current GridViewRow
TextBox productName = (TextBox)gvRow.FindControl("ProductName");
DropDownList categories =
(DropDownList)gvRow.FindControl("Categories");
TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
CheckBox discontinued =
(CheckBox)gvRow.FindControl("Discontinued");
// Assign the user-entered values to the current ProductRow
product.ProductName = productName.Text.Trim();
if (categories.SelectedIndex == 0)
product.SetCategoryIDNull();
else
product.CategoryID = Convert.ToInt32(categories.SelectedValue);
if (unitPrice.Text.Trim().Length == 0)
product.SetUnitPriceNull();
else
product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
product.Discontinued = discontinued.Checked;
}
}
// Now have the BLL update the products data using a transaction
productsAPI.UpdateWithTransaction(products);
}
此方法首先通过调用 BLL 方法GetProducts
将所有产品返回到 中ProductsDataTable
。 然后,ProductGrid
它枚举 GridView 集合Rows
。 集合Rows
包含 GridViewRow
GridView 中显示的每一行的 实例。 由于每页最多显示 10 行,因此 GridView 集合 Rows
的项不会超过 10 个。
对于每一行, ProductID
从集合中DataKeys
抓取,并从 ProductsDataTable
中选择适当的 ProductsRow
。 以编程方式引用四个 TemplateField 输入控件,并将其值分配给 ProductsRow
实例的 属性。 使用每个 GridView 行的值更新 ProductsDataTable
后,它将传递给 BLL 方法 UpdateWithTransaction
,正如我们在前面的教程中看到的那样,该方法只需向下调用 DAL s UpdateWithTransaction
方法。
本教程使用的批处理更新算法会更新 中 ProductsDataTable
对应于 GridView 中某一行的每一行,而不管产品信息是否已更改。 虽然此类盲目更新通常不是性能问题,但如果重新审核对数据库表的更改,它们可能会导致多余记录。 回到执行批处理汇报教程中,我们探索了使用 DataList 的批量更新界面,并添加了仅更新用户实际修改的记录的代码。 如果需要,可以随意使用执行批处理汇报中的技术更新本教程中的代码。
注意
通过智能标记将数据源绑定到 GridView 时,Visual Studio 会自动将数据源的主键值 () 分配给 GridView 属性 DataKeyNames
。 如果未按照步骤 1 中所述通过 GridView 智能标记将 ObjectDataSource 绑定到 GridView,则需要手动将 GridView 属性 DataKeyNames
设置为 ProductID,以便访问 ProductID
集合中 DataKeys
每一行的值。
中使用的BatchUpdate
代码与 BLL 方法UpdateProduct
中使用的代码类似,main不同之处在于,UpdateProduct
在 方法中,仅从体系结构中检索单个ProductRow
实例。 分配 属性的代码ProductRow
在 方法与 中的 BatchUpdate
循环中的foreach
代码之间UpdateProducts
相同,与整体模式相同。
若要完成本教程,我们需要在 BatchUpdate
单击“更新产品”按钮之一时调用 方法。 为 Click
这两个 Button 控件的事件创建事件处理程序,并在事件处理程序中添加以下代码:
BatchUpdate();
ClientScript.RegisterStartupScript(this.GetType(), "message",
"alert('The products have been updated.');", true);
首先调用 BatchUpdate
。 接下来, ClientScript property
用于注入 JavaScript,以显示一个显示“产品已更新”的消息框。
花一点时间测试此代码。 通过浏览器访问 BatchUpdate.aspx
,编辑多个行,然后单击其中一个“更新产品”按钮。 假设没有输入验证错误,应会看到一个显示“产品已更新”的消息框。 若要验证更新的原子性,请考虑添加一个随机 CHECK
约束,例如不允许 UnitPrice
值 1234.56 的约束。 然后,在 BatchUpdate.aspx
中编辑大量记录,确保将产品 UnitPrice
值之一设置为禁止值 ( 1234.56 ) 。 单击“更新产品”时,该批处理操作期间的其他更改回滚到其原始值时,这会导致错误。
替代BatchUpdate
方法
BatchUpdate
我们刚刚检查的方法从 BLL 方法GetProducts
检索所有产品,然后仅更新 GridView 中显示的记录。 如果 GridView 不使用分页,则此方法非常理想,但如果使用分页,则 GridView 中可能有数百、数千或数万个产品,但只有 10 行。 在这种情况下,仅从数据库中获取所有产品来修改其中 10 个产品并不理想。
对于这些类型的情况,请考虑改用以下 BatchUpdateAlternate
方法:
private void BatchUpdateAlternate()
{
// Enumerate the GridView's Rows collection and create a ProductRow
ProductsBLL productsAPI = new ProductsBLL();
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
foreach (GridViewRow gvRow in ProductsGrid.Rows)
{
// Create a new ProductRow instance
int productID = Convert.ToInt32(ProductsGrid.DataKeys[gvRow.RowIndex].Value);
Northwind.ProductsDataTable currentProductDataTable =
productsAPI.GetProductByProductID(productID);
if (currentProductDataTable.Rows.Count > 0)
{
Northwind.ProductsRow product = currentProductDataTable[0];
// Programmatically access the form field elements in the
// current GridViewRow
TextBox productName = (TextBox)gvRow.FindControl("ProductName");
DropDownList categories =
(DropDownList)gvRow.FindControl("Categories");
TextBox unitPrice = (TextBox)gvRow.FindControl("UnitPrice");
CheckBox discontinued =
(CheckBox)gvRow.FindControl("Discontinued");
// Assign the user-entered values to the current ProductRow
product.ProductName = productName.Text.Trim();
if (categories.SelectedIndex == 0)
product.SetCategoryIDNull();
else
product.CategoryID = Convert.ToInt32(categories.SelectedValue);
if (unitPrice.Text.Trim().Length == 0)
product.SetUnitPriceNull();
else
product.UnitPrice = Convert.ToDecimal(unitPrice.Text);
product.Discontinued = discontinued.Checked;
// Import the ProductRow into the products DataTable
products.ImportRow(product);
}
}
// Now have the BLL update the products data using a transaction
productsAPI.UpdateProductsWithTransaction(products);
}
BatchMethodAlternate
首先创建名为 products
的新空 ProductsDataTable
。 然后,它逐步执行 GridView 集合 Rows
,并使用 BLL 方法 GetProductByProductID(productID)
获取每一行的特定产品信息。 检索到的 ProductsRow
实例的属性更新方式BatchUpdate
与 相同,但在更新行后,将通过 DataTable 方法ImportRow(DataRow)
将其导入到 products``ProductsDataTable
中。
foreach
循环完成后, products
为 GridView 中的每一行包含一个ProductsRow
实例。 由于每个 ProductsRow
实例都已添加到 products
(而不是更新) ,因此如果我们盲目地将其传递给 UpdateWithTransaction
方法, ProductsTableAdapter
将尝试将每条记录插入数据库中。 相反,我们需要指定其中的每一行都已修改 (未添加) 。
这可以通过向名为 UpdateProductsWithTransaction
的 BLL 添加新方法来实现。 UpdateProductsWithTransaction
,如下所示,将 中Modified
ProductsDataTable
每个实例的 ProductsRow
设置为 RowState
,然后将 传递给 ProductsDataTable
DAL s UpdateWithTransaction
方法。
public int UpdateProductsWithTransaction(Northwind.ProductsDataTable products)
{
// Mark each product as Modified
products.AcceptChanges();
foreach (Northwind.ProductsRow product in products)
product.SetModified();
// Update the data via a transaction
return UpdateWithTransaction(products);
}
总结
GridView 提供内置的每行编辑功能,但不支持创建完全可编辑的接口。 正如我们在本教程中看到的,此类接口是可能的,但需要一些工作。 若要创建可编辑每一行的 GridView,我们需要将 GridView 的字段转换为 TemplateFields,并在 中 ItemTemplate
定义编辑界面。 此外,必须将“全部更新”类型的按钮 Web 控件添加到页面,与 GridView 分开。 这些 Buttons Click
事件处理程序需要枚举 GridView 集合 Rows
,将更改存储在 中 ProductsDataTable
,并将更新后的信息传递到相应的 BLL 方法中。
在下一教程中,我们将了解如何创建用于批量删除的接口。 具体而言,每个 GridView 行将包含一个复选框,我们将具有“删除所选行”按钮,而不是“全部更新”按钮。
编程愉快!
关于作者
Scott Mitchell 是七本 ASP/ASP.NET 书籍的作者, 4GuysFromRolla.com 的创始人,自 1998 年以来一直从事 Microsoft Web 技术工作。 Scott 担任独立顾问、培训师和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0自学。 可以在 上联系 mitchell@4GuysFromRolla.com他, 也可以通过他的博客联系到他,该博客可在 http://ScottOnWriting.NET中找到。
特别感谢
本教程系列由许多有用的审阅者查看。 本教程的主要审阅者是 Teresa Murphy 和 David Suru。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处放置一行 mitchell@4GuysFromRolla.com。