执行批量更新 (VB)

作者 :Scott Mitchell

下载 PDF

了解如何创建一个完全可编辑的 DataList,其中所有项都处于编辑模式,并且可以通过单击页面上的“全部更新”按钮保存其值。

简介

前面的教程中 ,我们检查了如何创建项级 DataList。 与标准可编辑 GridView 一样,DataList 中的每个项都包含一个“编辑”按钮,单击该按钮时,该项目可编辑。 虽然此项级编辑适用于仅偶尔更新的数据,但某些用例方案需要用户编辑许多记录。 如果用户需要编辑数十条记录,并且被迫单击“编辑”,进行更改,然后单击每个记录的“更新”,则单击量可能会妨碍她的工作效率。 在这种情况下,更好的选择是提供完全可编辑的 DataList,该列表 的所有 项都处于编辑模式,并且可以通过单击页面上的“全部更新”按钮进行编辑, (见图 1) 。

可以修改完全可编辑 DataList 中的每个项

图 1:可以修改完全可编辑 DataList 中的每个项 (单击以查看全尺寸图像)

本教程介绍如何让用户使用完全可编辑的 DataList 更新供应商地址信息。

步骤 1:在 DataList 的 ItemTemplate 中创建可编辑的用户界面

在前面的教程中,我们创建了一个标准的项级可编辑 DataList,我们使用了两个模板:

  • ItemTemplate 包含只读用户界面 (用于显示每个产品名称和价格) 的标签 Web 控件。
  • EditItemTemplate 包含编辑模式用户界面 (两个 TextBox Web 控件) 。

DataList 的 EditItemIndex 属性指定 DataListItem 使用 EditItemTemplate呈现任何) 时 (。 具体而言, DataListItem 使用 呈现其 ItemIndex 值与 DataList 属性匹配的 EditItemIndexEditItemTemplate。 如果一次只能编辑一项,但在创建完全可编辑的 DataList 时,此模型就很有效。

对于完全可编辑的 DataList,我们希望使用可编辑接口呈现 所有DataListItem 。 实现此目的的最简单方法是在 中定义可编辑的 ItemTemplate接口。 为了修改供应商地址信息,可编辑的界面以文本的形式包含供应商名称,然后包含地址、城市和国家/地区值的 TextBoxes。

首先打开 BatchUpdate.aspx 页面,添加 DataList 控件,并将其 属性设置为 IDSuppliers。 在 DataList 的智能标记中,选择添加名为 SuppliersDataSource的新 ObjectDataSource 控件。

新建名为 SuppliersDataSource 的 ObjectDataSource

图 2:创建名为 SuppliersDataSource 的新对象DataSource (单击以查看全尺寸图像)

配置 ObjectDataSource 以使用 SuppliersBLL 类方法 GetSuppliers() 检索数据 (请参阅图 3) 。 与前面的教程一样,我们将直接使用业务逻辑层,而不是通过 ObjectDataSource 更新供应商信息。 因此,请在“更新”选项卡中将下拉列表设置为“ (无”) (请参阅图 4) 。

使用 GetSuppliers () 方法检索供应商信息

图 3:使用 GetSuppliers() 方法检索供应商信息 (单击以查看全尺寸图像)

在“更新”选项卡中将“Drop-Down 列表”设置为“ (无”)

图 4:在“更新”选项卡中将“Drop-Down 列表”设置为“无” () (单击以查看全尺寸图像)

完成向导后,Visual Studio 会自动生成 DataList, ItemTemplate 以显示标签 Web 控件中数据源返回的每个数据字段。 我们需要修改此模板,使其改为提供编辑界面。 ItemTemplate可以通过 Designer使用 DataList 智能标记中的“编辑模板”选项或直接通过声明性语法自定义 。

花点时间创建一个编辑界面,该界面以文本形式显示供应商名称,但包含供应商地址、城市和国家/地区值的 TextBox。 进行这些更改后,页面 的声明性语法应如下所示:

<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 类, SupplierPropertyLabelSupplierPropertyValue,它们已添加到 类中Styles.css,并配置为使用与 和 ProductPropertyValue CSS 类相同的样式设置ProductPropertyLabel

.ProductPropertyLabel, .SupplierPropertyLabel
{
    font-weight: bold;
    text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
    padding-right: 35px;
}

进行这些更改后,请通过浏览器访问此页面。 如图 5 所示,每个 DataList 项将供应商名称显示为文本,并使用 TextBoxes 显示地址、城市和国家/地区。

DataList 中的每个供应商都是可编辑的

图 5:DataList 中的每个供应商都可以编辑 (单击以查看全尺寸图像)

步骤 2:添加“全部更新”按钮

虽然图 5 中的每个供应商的地址、城市和国家/地区字段都显示在 TextBox 中,但目前没有可用的“更新”按钮。 对于完全可编辑的 DataList,通常页面上有一个“全部更新”按钮,单击该按钮时会更新 DataList 中的所有 记录,而不是每个项都有一个“更新”按钮。 在本教程中,让我们添加两个“全部更新”按钮 - 一个位于页面顶部,一个位于底部 (尽管单击任一按钮) 具有相同的效果。

首先,在 DataList 上方添加一个 Button Web 控件,并将其 ID 属性设置为 UpdateAll1。 接下来,在 DataList 下添加第二个 Button 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 的实例集合DataListItemItems。 通过对 的DataListItem引用,我们可以从 集合中DataKeys获取相应的 SupplierID ,并按编程方式引用 中的 ItemTemplate TextBox Web 控件,如以下代码所示:

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 ,并传入相应的值。 地址、城市或国家/地区传递的未输入值是 (UpdateSupplierAddress 的值Nothing,而不是空字符串) ,这将导致基础记录字段的数据库NULL

注意

作为增强功能,你可能想要将状态标签 Web 控件添加到执行批处理更新后提供一些确认消息的页面。

仅更新那些已修改的地址

本教程使用的批量更新算法为 DataList 中的每个供应商调用 UpdateSupplierAddress 方法,无论其地址信息是否已更改。 虽然此类盲目更新通常不是性能问题,但如果重新审核对数据库表的更改,它们可能会导致多余记录。 例如,如果使用触发器将表中的所有 UPDATESuppliers 记录到审核表,则每当用户单击“全部更新”按钮时,都会为系统中的每个供应商创建新的审核记录,无论用户是否进行了任何更改。

ADO.NET DataTable 和 DataAdapter 类旨在支持批处理更新,其中只有修改、删除和新记录会导致任何数据库通信。 DataTable 中的每一行都有一个 RowState 属性 ,该属性指示该行是已添加到 DataTable、从其中删除、修改还是保持不变。 最初填充 DataTable 时,所有行都标记为不变。 更改任何行列的值会将该行标记为已修改。

SuppliersBLL在 类中,我们更新指定的供应商地址信息,方法是先将单个供应商记录读取到 中SuppliersDataTable,然后使用以下代码设置 AddressCityCountry 列值:

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

此代码以朴素的方式将传入的地址、城市和国家/地区值 SuppliersRow 分配给 中的 SuppliersDataTable ,而不考虑值是否已更改。 这些修改会导致 SuppliersRow 将 s RowState 属性标记为已修改。 调用数据访问层 方法 Update 时,它会看到 SupplierRow 已修改,因此向数据库发送 UPDATE 命令。

但是,假设我们在此方法中添加了代码,以便仅分配传入的地址、城市和国家/地区值(如果它们与 SuppliersRow 现有值不同)。 如果地址、城市和国家/地区与现有数据相同,则不会进行更改,SupplierRowRowState并且 会将 标记为未更改。 最终结果是,调用 DAL s 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 s 方法的 Update 调用。 如果使用 DB 直接方法,此方法非常有效,因为 DB 直接方法不会传递 SuppliersRow 实例,该实例可以检查该实例 RowState 以确定实际是否需要数据库调用。

注意

每次调用 方法时 UpdateSupplierAddress ,都会对数据库进行调用,以检索有关更新的记录的信息。 然后,如果数据有任何更改,则再次调用数据库以更新表行。 可以通过创建一个 UpdateSupplierAddress 方法重载来优化此工作流,该方法重载接受 EmployeesDataTable 包含页面中 所有 更改 BatchUpdate.aspx 的实例。 然后,它可以对数据库进行一次调用,以获取表中的所有 Suppliers 记录。 然后可以枚举两个结果集,并且只能更新发生更改的记录。

总结

在本教程中,我们了解了如何创建完全可编辑的 DataList,允许用户快速修改多个供应商的地址信息。 我们首先为 DataList ItemTemplate中的供应商地址、城市和国家/地区值定义 TextBox Web 控件的编辑界面。 接下来,我们在 DataList 的上方和下方添加了“全部更新”按钮。 用户进行更改并单击其中一个“全部更新”按钮后, DataListItem 将枚举 , 并调用 SuppliersBLL 类的 UpdateSupplierAddress 方法。

编程快乐!

关于作者

斯科特·米切尔是七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直在使用 Microsoft Web 技术。 Scott 担任独立顾问、培训师和作家。 他的最新一本书是 山姆斯在 24 小时内 ASP.NET 2.0。 可以在 上mitchell@4GuysFromRolla.com联系他,也可以通过他的博客(可在 中找到http://ScottOnWriting.NET)。

特别感谢

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Zack Jones 和 Ken Pespisa。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处mitchell@4GuysFromRolla.com放置一行。