执行批量更新 (VB)

作者 :斯科特·米切尔

下载 PDF

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

介绍

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

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

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

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

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

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

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

DataList s 属性指示使用 EditItemTemplateEditItemIndex> 呈现的内容DataListItem(如果有)。 具体而言, DataListItemItemIndex 值与 DataList 属性 EditItemIndex 匹配的呈现方式 EditItemTemplate是使用 . 当一次只能编辑一个项,但在创建完全可编辑的 DataList 时,此模型可正常工作。

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

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

创建名为 SuppliersDataSource 的新 ObjectDataSource

图 2:创建名为 SuppliersDataSource 的新 ObjectDataSource (单击可查看全尺寸图像

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

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

图 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 类相同的样式设置ProductPropertyLabelProductPropertyValue

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

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

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

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

注意

作为增强功能,你可能希望向页面添加状态标签 Web 控件,该页面在执行批处理更新后提供一些确认消息。

仅更新已修改的地址

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

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

无论值是否已更改,此代码都会将传入的地址、城市和国家/地区值分配给传入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 直接方法未通过SuppliersRowRowState实例进行检查以确定是否实际需要数据库调用。

注意

每次调用该方法时 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