执行批量更新 (VB)
作者 :斯科特·米切尔
了解如何创建完全可编辑的 DataList,其中所有项都处于编辑模式,并且可以通过单击页面上的“全部更新”按钮保存其值。
介绍
在前面的教程中,我们研究了如何创建项目级 DataList。 与标准可编辑 GridView 一样,DataList 中的每个项都包含一个“编辑”按钮,单击该按钮会使项目可编辑。 虽然此项级编辑适用于仅偶尔更新的数据,但某些用例方案要求用户编辑许多记录。 如果用户需要编辑数十条记录,并被迫单击“编辑”,进行更改,然后单击每个记录的“更新”,则单击次数可能会阻碍她的工作效率。 在这种情况下,更好的选择是提供完全可编辑的 DataList,其中 所有 项都处于编辑模式,并且可以通过单击页面上的“全部更新”按钮来编辑其值(请参阅图 1)。
图 1:可以修改完全可编辑 DataList 中的每个项(单击以查看全尺寸图像)
本教程介绍如何让用户使用完全可编辑的 DataList 更新供应商地址信息。
步骤 1:在 DataList s ItemTemplate 中创建可编辑用户界面
在前面的教程中,我们在创建标准项级可编辑 DataList 时使用了两个模板:
ItemTemplate
包含只读用户界面(用于显示每个产品名称和价格的标签 Web 控件)。EditItemTemplate
包含编辑模式用户界面(两个 TextBox Web 控件)。
DataList s 属性指示使用 EditItemTemplate
DataListItem
(如果有)。 具体而言, DataListItem
其 ItemIndex
值与 DataList 属性 EditItemIndex
匹配的呈现方式 EditItemTemplate
是使用 . 当一次只能编辑一个项,但在创建完全可编辑的 DataList 时,此模型可正常工作。
对于完全可编辑的 DataList,我们希望 所有 DataListItem
S 都使用可编辑接口呈现。 实现此目的的最简单方法是在 中 ItemTemplate
定义可编辑接口。 对于修改供应商地址信息,可编辑接口包含供应商名称作为文本,然后包含地址、城市和国家/地区值的 TextBoxes。
首先打开 BatchUpdate.aspx
页面,添加 DataList 控件,并将其属性设置为 ID
Suppliers
。 从 DataList 的智能标记中,选择添加名为 SuppliersDataSource
的新 ObjectDataSource 控件。
图 2:创建名为 SuppliersDataSource
的新 ObjectDataSource (单击可查看全尺寸图像)
将 ObjectDataSource 配置为使用 SuppliersBLL
类 s GetSuppliers()
方法检索数据(请参阅图 3)。 与前面的教程一样,我们将直接使用业务逻辑层,而不是通过 ObjectDataSource 更新供应商信息。 因此,将“更新”选项卡中的下拉列表设置为“无”(请参阅图 4)。
图 3:使用 GetSuppliers()
方法检索供应商信息(单击以查看全尺寸图像)
图 4:将“更新”选项卡中的下拉列表设置为“无”(单击以查看全尺寸图像)
完成向导后,Visual Studio 自动生成 DataList, ItemTemplate
以显示标签 Web 控件中数据源返回的每个数据字段。 我们需要修改此模板,以便它改为提供编辑界面。 ItemTemplate
可以使用 DataList 智能标记中的“编辑模板”选项或通过声明性语法直接通过设计器自定义。
花点时间创建一个编辑界面,该界面将供应商的名称显示为文本,但包括供应商地址、城市和国家/地区值的 TextBoxes。 进行这些更改后,页面的声明性语法应如下所示:
<asp:DataList ID="Suppliers" runat="server" DataKeyField="SupplierID"
DataSourceID="SuppliersDataSource">
<ItemTemplate>
<h4><asp:Label ID="CompanyNameLabel" runat="server"
Text='<%# Eval("CompanyName") %>' /></h4>
<table border="0">
<tr>
<td class="SupplierPropertyLabel">Address:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Address" runat="server"
Text='<%# Eval("Address") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">City:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="City" runat="server"
Text='<%# Eval("City") %>' />
</td>
</tr>
<tr>
<td class="SupplierPropertyLabel">Country:</td>
<td class="SupplierPropertyValue">
<asp:TextBox ID="Country" runat="server"
Text='<%# Eval("Country") %>' />
</td>
</tr>
</table>
<br />
</ItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="SuppliersDataSource" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetSuppliers" TypeName="SuppliersBLL">
</asp:ObjectDataSource>
注意
与前面的教程一样,本教程中的 DataList 必须启用其视图状态。
在ItemTemplate
我使用的是两个新的 CSS 类,SupplierPropertyLabel
并且SupplierPropertyValue
已添加到Styles.css
该类,并配置为使用与 CSS 类相同的样式设置ProductPropertyLabel
ProductPropertyValue
。
.ProductPropertyLabel, .SupplierPropertyLabel
{
font-weight: bold;
text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
padding-right: 35px;
}
进行这些更改后,通过浏览器访问此页面。 如图 5 所示,每个 DataList 项将供应商名称显示为文本,并使用 TextBoxes 显示地址、城市和国家/地区。
图 5:DataList 中的每个供应商都是可编辑的(单击以查看全尺寸图像)
步骤 2:添加“全部更新”按钮
虽然图 5 中的每个供应商都有其地址、城市和国家/地区字段显示在 TextBox 中,但目前没有可用的“更新”按钮。 通常页面上有一个“全部更新”按钮,而不是每个项目都有一个“更新”按钮,其中通常有一个“更新”按钮,单击后,更新 DataList 中的所有 记录。 在本教程中,让我们添加两个“全部更新”按钮-一个位于页面顶部,一个位于底部(尽管单击任一按钮将具有相同的效果)。
首先,在 DataList 上方添加一个 Button Web 控件,并将其 ID
属性设置为 UpdateAll1
。 接下来,在 DataList 下面添加第二个按钮 Web 控件,将其 ID
设置为 UpdateAll2
。 将 Text
两个按钮的属性设置为“全部更新”。 最后,为这两个 Buttons Click
事件创建事件处理程序。 让我们将该逻辑重构到第三个方法,而不是复制每个事件处理程序中的更新逻辑, UpdateAllSupplierAddresses
让事件处理程序只调用第三种方法。
Protected Sub UpdateAll1_Click(sender As Object, e As EventArgs) _
Handles UpdateAll1.Click
UpdateAllSupplierAddresses()
End Sub
Protected Sub UpdateAll2_Click(sender As Object, e As EventArgs) _
Handles UpdateAll2.Click
UpdateAllSupplierAddresses()
End Sub
Private Sub UpdateAllSupplierAddresses()
' TODO: Write code to update _all_ of the supplier addresses in the DataList
End Sub
图 6 显示添加“全部更新”按钮后的页面。
图 6:已将两个更新所有按钮添加到页面(单击以查看全尺寸图像)
步骤 3:更新所有供应商地址信息
由于所有 DataList 项都显示编辑界面,并添加了“全部更新”按钮,剩下的所有项都是编写代码来执行批处理更新。 具体而言,我们需要循环访问 DataList 项,并为每个项调用 SuppliersBLL
类 s UpdateSupplierAddress
方法。
可以通过 DataList 属性访问构成 DataList 的Items
实例集合。DataListItem
通过对 a DataListItem
的引用,我们可以从DataKeys
集合中获取相应的内容SupplierID
,并按编程方式引用 TextBox Web 控件,ItemTemplate
如以下代码所示:
Private Sub UpdateAllSupplierAddresses()
' Create an instance of the SuppliersBLL class
Dim suppliersAPI As New SuppliersBLL()
' Iterate through the DataList's items
For Each item As DataListItem In Suppliers.Items
' Get the supplierID from the DataKeys collection
Dim supplierID As Integer = Convert.ToInt32(Suppliers.DataKeys(item.ItemIndex))
' Read in the user-entered values
Dim address As TextBox = CType(item.FindControl("Address"), TextBox)
Dim city As TextBox = CType(item.FindControl("City"), TextBox)
Dim country As TextBox = CType(item.FindControl("Country"), TextBox)
Dim addressValue As String = Nothing, _
cityValue As String = Nothing, _
countryValue As String = Nothing
If address.Text.Trim().Length > 0 Then
addressValue = address.Text.Trim()
End If
If city.Text.Trim().Length > 0 Then
cityValue = city.Text.Trim()
End If
If country.Text.Trim().Length > 0 Then
countryValue = country.Text.Trim()
End If
' Call the SuppliersBLL class's UpdateSupplierAddress method
suppliersAPI.UpdateSupplierAddress _
(supplierID, addressValue, cityValue, countryValue)
Next
End Sub
当用户单击“全部更新”按钮之一时,该方法 UpdateAllSupplierAddresses
循环访问 DataListItem
DataList 中的每个 Suppliers
按钮并调用 SuppliersBLL
类的方法 UpdateSupplierAddress
,并传入相应的值。 地址、城市或国家/地区传递的非输入值是一个值 Nothing
( UpdateSupplierAddress
而不是空白字符串),这会导致基础记录字段的数据库 NULL
。
注意
作为增强功能,你可能希望向页面添加状态标签 Web 控件,该页面在执行批处理更新后提供一些确认消息。
仅更新已修改的地址
本教程中使用的批处理更新算法调用 UpdateSupplierAddress
DataList 中每个 供应商的方法,无论其地址信息是否已更改。 虽然此类盲目更新通常不是性能问题,但如果正在审核数据库表的更改,它们可能会导致多余的记录。 例如,如果使用触发器将所有 UPDATE
记录到 Suppliers
审核表中,则每当用户单击“全部更新”按钮时,系统中的每个供应商都会创建新的审核记录,而不管用户是否进行任何更改。
ADO.NET DataTable 和 DataAdapter 类旨在支持仅修改、删除和新记录会导致任何数据库通信的批处理更新。 DataTable 中的每个行都有一个 RowState
属性,该属性 指示该行是否已添加到 DataTable、从其中删除、修改或保持不变。 最初填充 DataTable 时,所有行都标记为未更改。 更改任何行列的值会将行标记为已修改。
在类中SuppliersBLL
,我们先在单个供应商记录SuppliersDataTable
中读取指定的供应商地址信息,然后使用以下代码设置Address
和City
Country
列值:
Public Function UpdateSupplierAddress _
(supplierID As Integer, address As String, city As String, country As String) _
As Boolean
Dim suppliers As Northwind.SuppliersDataTable = _
Adapter.GetSupplierBySupplierID(supplierID)
If suppliers.Count = 0 Then
' no matching record found, return false
Return False
Else
Dim supplier As Northwind.SuppliersRow = suppliers(0)
If address Is Nothing Then
supplier.SetAddressNull()
Else
supplier.Address = address
End If
If city Is Nothing Then
supplier.SetCityNull()
Else
supplier.City = city
End If
If country Is Nothing Then
supplier.SetCountryNull()
Else
supplier.Country = country
End If
' Update the supplier Address-related information
Dim rowsAffected As Integer = Adapter.Update(supplier)
' Return true if precisely one row was updated, otherwise false
Return rowsAffected = 1
End If
End Function
无论值是否已更改,此代码都会将传入的地址、城市和国家/地区值分配给传入SuppliersDataTable
的地址、城市和国家/地区值SuppliersRow
。 这些修改会导致 SuppliersRow
将 s RowState
属性标记为已修改。 调用数据访问层的方法 Update
时,会看到 SupplierRow
该方法已修改,因此会向数据库发送命令 UPDATE
。
但是,假设我们向此方法添加了代码,以便在传入地址、城市和国家/地区值与 SuppliersRow
现有值不同时仅分配传入地址、城市和国家/地区值。 如果地址、城市和国家/地区与现有数据相同,则不会进行 SupplierRow
任何更改,并且 s RowState
将标记为未更改。 净结果是,调用 DAL 方法 Update
时,不会进行任何数据库调用,因为 SuppliersRow
尚未修改数据库。
若要执行此更改,请将盲目分配传入地址、城市和国家/地区值的语句替换为以下代码:
' Only assign the values to the SupplierRow's column values if they differ
If address Is Nothing AndAlso Not supplier.IsAddressNull() Then
supplier.SetAddressNull()
ElseIf (address IsNot Nothing AndAlso supplier.IsAddressNull) _
OrElse (Not supplier.IsAddressNull() AndAlso _
String.Compare(supplier.Address, address) <> 0) Then
supplier.Address = address
End If
If city Is Nothing AndAlso Not supplier.IsCityNull() Then
supplier.SetCityNull()
ElseIf (city IsNot Nothing AndAlso supplier.IsCityNull) _
OrElse (Not supplier.IsCityNull() AndAlso _
String.Compare(supplier.City, city) <> 0) Then
supplier.City = city
End If
If country Is Nothing AndAlso Not supplier.IsCountryNull() Then
supplier.SetCountryNull()
ElseIf (country IsNot Nothing AndAlso supplier.IsCountryNull) _
OrElse (Not supplier.IsCountryNull() AndAlso _
String.Compare(supplier.Country, country) <> 0) Then
supplier.Country = country
End If
借助此添加的代码,DAL s Update
方法仅针对地址相关值已更改的记录向数据库发送 UPDATE
语句。
或者,我们可以跟踪传入的地址字段与数据库数据之间是否存在任何差异,如果没有,只需绕过对 DAL 方法的 Update
调用。 如果使用 DB 直接方法,则此方法非常有效,因为 DB 直接方法未通过SuppliersRow
RowState
实例进行检查以确定是否实际需要数据库调用。
注意
每次调用该方法时 UpdateSupplierAddress
,都会调用数据库以检索有关更新记录的信息。 然后,如果数据有任何更改,则会对数据库进行另一次调用以更新表行。 可以通过创建接受UpdateSupplierAddress
包含页面所有更改BatchUpdate.aspx
的实例的方法重载EmployeesDataTable
来优化此工作流。 然后,它可以对数据库进行一次调用,以便从 Suppliers
表中获取所有记录。 然后,可以枚举这两个结果集,并且只能更新发生更改的记录。
总结
本教程介绍了如何创建完全可编辑的 DataList,允许用户快速修改多个供应商的地址信息。 我们首先在 DataList s ItemTemplate
中为供应商地址、城市和国家/地区值定义 TextBox Web 控件的编辑界面。 接下来,我们在 DataList 前面和下方添加了“更新所有”按钮。 用户进行更改并单击“全部更新”按钮之一后, DataListItem
将枚举 s 并调用 SuppliersBLL
类的方法 UpdateSupplierAddress
。
快乐编程!
关于作者
斯科特·米切尔,七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 山姆斯在24小时内 ASP.NET 2.0。 他可以通过他的博客联系到mitchell@4GuysFromRolla.com他,可以在该博客中找到http://ScottOnWriting.NET。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Zack Jones 和 Ken Pespisa。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请把我扔一条线。mitchell@4GuysFromRolla.com