执行批处理更新37

简介

在 前一篇教程 中,我们介绍了如何创建记录级 DataList 。如同标准的可编辑 GridView 一样, DataList 中的每个记录都包含一个 Edit 按钮,当单击该按钮时将使记录变为可编辑的。然而此记录级的编辑适用于偶尔更新的数据,但是某些案例场景需要用户编辑许多记录。如果用户需要编辑许多记录,不得不每次都单击 Edit ,进行更改,然后单击 Update ,大量的单击数量将会妨碍生产效率。在这种情况下,一种更好的选择是提供完全可编辑的 DataList ,它的所有记录都处于编辑模式,并且它的值可以通过单击页面上的 “Update All” 按钮来进行编辑(参见图 1 )。

图1 :完全可编辑的 DataList 中的每个记录

本教程中,我们将介绍如何使用户通过完全可编辑的DataList 来更新供应商的地址信息。

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

在前一篇教程中,我们创建标准的、记录级可编辑 DataLis t 时,使用了两个模板:

  • ItemTemplate—— 包含只读用户界面(Web Label 控件用于显示每种产品的名称和价格)。
  • EditItemTemplate—— 包含编辑模式用户界面(2 个 Web 文本框 控件)。

DataList 的 EditItemIndex 属性规定哪个 DataListItem (如果有)使用 EditItemTemplate 来呈现。特别是,ItemIndex 值与 DataList 的 EditItemIndex 属性相匹配的 DataListItem 使用 EditItemTemplate 来呈现。当一次只能编辑一个记录时,此模型能很好地工作,但是当创建完全可编辑的 DataList 时,此模型会崩溃。

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

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

图2 : 新建一个名称为 SuppliersDataSource 的ObjectDataSource

将 ObjectDataSource 配置为使用 SuppliersBLL 类的 GetSuppliers() 来检索数据(参见图 3)如同前面的教程一样,不是通过 ObjectDataSource 来更新供应商的信息,我们将直接使用业务逻辑层。因此,在 UPDATE 选项卡中将下拉列表设置为 (None) (参见图 4 )。

图3 :使用 GetSuppliers() 方法来检索供应商信息

图4 :在 UPDATE 选项卡中将下拉列表设置为 (None)

完成向导之后,Visual Studio 将自动生成 DataList 的 ItemTemplate 以便在 Web Label 控件中显示数据源返回的每个数据字段。需要修改此模板以便它可以提供编辑界面。通过设计器使用 Edit Templates 选项从 DataList 的智能标记或直接通过声明式语法可以自定义 ItemTemplate 。

花些时间创建可以将供应商的名称显示为文本的编辑界面,还包括用于修改供应商的地址、城市和国家值的 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 类 SupplierPropertyLabel 和 SupplierPropertyValue ,它们已经被添加到 Styles.css 类并使其使用与 ProductPropertyLabel 和 ProductPropertyValue CSS 类相同的样式设置。

.ProductPropertyLabel, .SupplierPropertyLabel 

    font-weight: bold; 
    text-align: right; 

 
.ProductPropertyValue, .SupplierPropertyValue 

    padding-right: 35px; 
}

进行这些更改之后,通过浏览器访问此页面。如图 5 所示,每个 DataList 项以文本形式显示供应商的名称,使用 TextBox 显示地址、城市和国家。

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

步骤2 :添加“Update All” 按钮

虽然在图 5 中每个供应商的地址、城市和国家字段都显示在TextBox 中,但目前还没有 Update 按钮可供使用。这里不是使每个记录都有一个 Update 按钮,使用完全可编辑 DataList ,通常在页面上只有一个 “Update All” 按钮,当单击它时可以更新 DataList 中的所有记录。对于本教程,我们添加两个 “Update All” 按钮 —— 一个位于页面的顶部,另一个位于页面的底部(单击任何一个按钮都会得到同样的效果)。

首先在 DataList 的上面添加一个 Web 按钮 控件并将其 ID 属性设置为 UpdateAll1 。然后,在DataList 的下面添加第二个 Web 按钮 控件,将其ID 设置为 UpdateAll2 。将两个按钮的Text 属性设置为 “Update All” 。最后,为两个按钮的Click 事件创建 event handler 。在这里,我们不是在每个event handler 中复制更新逻辑,而是将该逻辑重构为第三个方法 UpdateAllSupplierAddresses ,使 event handler 调用这个方法。

protected void UpdateAll1_Click(object sender, EventArgs e) 

    UpdateAllSupplierAddresses(); 

 
protected void UpdateAll2_Click(object sender, EventArgs e) 

    UpdateAllSupplierAddresses(); 

 
private void UpdateAllSupplierAddresses() 

    // TODO: Write code to update _all_ of the supplier addresses in the DataList 
}

图6 显示的添加 “Update All” 按钮之后的页面。

图6 :两个“Update All” 按钮已添加到页面

步骤3 :更新所有供应商的地址信息

所有 DataList 的记录都显示编辑界面和添加 “Update All” 按钮之后,所剩的工作只是编写代码以执行批更新。特别地,需要遍历 DataList 的记录并为每个记录调用 SuppliersBLL 类的 UpdateSupplierAddress 方法。

组成DataList 的DataListItem 实例的集合可以通过 DataList 的 Items 属性 来访问。通过对 DataListItem 的引用,可以从 DataKeys 集合获取相应的SupplierID 并在ItemTemplate 内以编程方式引用 Web 文本框 控件,如以下代码所示:

private void UpdateAllSupplierAddresses() 

    // Create an instance of the SuppliersBLL class 
    SuppliersBLL suppliersAPI = new SuppliersBLL(); 
 
    // Iterate through the DataList's items 
    foreach (DataListItem item in Suppliers.Items) 
    { 
        // Get the supplierID from the DataKeys collection 
        int supplierID = Convert.ToInt32(Suppliers.DataKeys[item.ItemIndex]); 
 
        // Read in the user-entered values 
        TextBox address = (TextBox)item.FindControl("Address"); 
        TextBox city = (TextBox)item.FindControl("City"); 
        TextBox country = (TextBox)item.FindControl("Country"); 
 
        string addressValue = null, cityValue = null, countryValue = null; 
        if (address.Text.Trim().Length > 0) 
            addressValue = address.Text.Trim(); 
        if (city.Text.Trim().Length > 0) 
              cityValue = city.Text.Trim(); 
        if (country.Text.Trim().Length > 0) 
            countryValue = country.Text.Trim(); 
 
        // Call the SuppliersBLL class's UpdateSupplierAddress method 
        suppliersAPI.UpdateSupplierAddress 
            (supplierID, addressValue, cityValue, countryValue); 
    } 
}

当用户单击一个 “Update All” 按钮时,UpdateAllSupplierAddresses 方法将在 Suppliers DataList 中遍历每个 DataListItem 并调用 SuppliersBLL 类的 UpdateSupplierAddress 方法,传递相应的值。地址、城市或国家的非输入值向 UpdateSupplierAddress 传递一个 Nothing 值(而不是空字符串),这将导致底层数据字段的 NULL 值。

注意 :做为增强,可以向页面添加状态 Web Label 控件,它可以在执行批处理更新之后提供一些确认消息。

只更新那些修改过的地址

本教程使用的批处理更新算法为 DataList 中的每个供应商调用 UpdateSupplierAddress 方法,而不管它们的地址信息是否更改。虽然这种盲目更新通常不会有性能问题,但是如果对数据库表的更改进行监控的话,它们可能导致冗余的记录。例如,如果使用触发器将所有的 Suppliers 表的 UPDATE 记录到监控表中,用户每次单击 “Update All” 按钮,都会为系统中的每个供应商创建一个新的监控记录,而不管用户是否进行任何更改。

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

在 SuppliersBLL 类中,通过以下方式更新指定的供应商的地址信息,首先将单个供应商的记录读入到 SuppliersDataTable 中,然后使用以下代码设置 Address 、 City 和 Country 列:

public bool UpdateSupplierAddress 
    (int supplierID, string address, string city, string country) 

    Northwind.SuppliersDataTable suppliers = 
        Adapter.GetSupplierBySupplierID(supplierID); 
    if (suppliers.Count == 0) 
        // no matching record found, return false 
        return false; 
    else 
    { 
        Northwind.SuppliersRow supplier = suppliers[0]; 
 
        if (address == null) 
            supplier.SetAddressNull(); 
        else 
            supplier.Address = address; 
        if (city == null) 
            supplier.SetCityNull(); 
        else 
            supplier.City = city; 
        if (country == null) 
            supplier.SetCountryNull(); 
        else 
            supplier.Country = country; 
 
        // Update the supplier Address-related information 
        int rowsAffected = Adapter.Update(supplier); 
 
        // Return true if precisely one row was updated, 
        // otherwise false 
        return rowsAffected == 1; 
    } 
}

此代码将传入的地址、城市和国家值分配给SuppliersDataTable 中的 SuppliersRow ,而不管值是否已经改变。这些修改致使 SuppliersRow 的 RowState 属性被标记为修改。当调用 Data Access Layer 的 Update 方法时,它看到 SupplierRow 被修改,因而向数据库发送 UPDATE 命令。

但是,假设我们向此方法添加代码以便只当传入的地址、城市和国家值不同于 SuppliersRow 的现有值时才分配它们。当地址、城市和国家与现有数据相同时,不进行更改并且 SupplierRow 的 RowState 仍标记为不变。最终的结果是,当调用 DAL 的 Update 方法时,没有进行数据库调用,因为 SuppliersRow 还未被修改。

为了实现这种更改,使用以下代码替换盲目分配传入的地址、城市和国家值的语句:

// Only assign the values to the SupplierRow's column values if they differ 
if (address == null && !supplier.IsAddressNull()) 
    supplier.SetAddressNull(); 
else if ((address != null && supplier.IsAddressNull()) || 
         (!supplier.IsAddressNull() && 
         string.Compare(supplier.Address, address) != 0)) 
    supplier.Address = address; 
 
if (city == null && !supplier.IsCityNull()) 
    supplier.SetCityNull(); 
else if ((city != null && supplier.IsCityNull()) || 
         (!supplier.IsCityNull() && string.Compare(supplier.City, city) != 0)) 
    supplier.City = city; 
 
if (country == null && !supplier.IsCountryNull()) 
    supplier.SetCountryNull(); 
else if ((country != null && supplier.IsCountryNull()) || 
         (!supplier.IsCountryNull() && 
         string.Compare(supplier.Country, country) != 0)) 
    supplier.Country = country;

通过此添加的代码,DAL 的 Update 方法只为那些与地址有关的值已改变的记录向数据库发送UPDATE 语句。

或者,可以对传入的地址字段和数据库数据之间是否不同进行跟踪,如果没有不同,则将调用传到 DAL 的 Update 方法。如果使用 DB 直接方法,此方法能很好地工作,因为 DB 直接方法不传递下面这种 SuppliersRow 实例,即它的 RowState 可以被检查以确定是否实际需要数据库调用。

注意 :每次调用 UpdateSupplierAddress 方法,都对数据库进行一次调用以检索有关更新的记录的信息。如果数据有任何更改,则对数据库进行另一次调用以更新表行。通过创建接受包含所有来自 BatchUpdate.aspx 更改的 SupplierDataTable 实例的 UpdateSupplierAddress 方法重载,可以优化此工作流程。然后,它可以对数据库进行一次调用以便从 Suppliers 表获得所有的记录。然后可以枚举两个结果集,只有那些出现更改的记录才可以被更新。

小结

本教程中,我们了解了怎样创建完全可编辑的 DataList ,从而使用户可以快速编辑多个供应商的地址信息。我们首先在 DataList 的 ItemTemplate 中定义编辑界面—— 用于供应商地址、城市和国家值的 Web 文本框 控件。然后,在 DataList 的上面和下面添加 “Update All” 按钮。当用户进行更改并单击其中的一个 “Update All” 按钮之后,将会对 DataListItems 进行枚举并对 SuppliersBLL 类的 UpdateSupplierAddress 方法进行调用。

快乐编程!

posted @ 2016-05-01 23:17  迅捷之风  阅读(106)  评论(0编辑  收藏  举报