导言
在前面我们学习了如何创建item级的DataList。和可编辑的GridView一样,每个DataList里的item都包含一个Edit button,当点击时,item会变的可编辑。item级的编辑在偶尔需要更新时没什么问题,但是在有些情况下用户需要编辑大量的记录。如果一个用户需要编辑许多记录,他会被迫去不停的去点击Edit,作出修改,然后点击Update,这些大量的点击会妨碍他的工作效率。在这样的情况下,一个好的选择是提供一个完全可编辑的DataList,它的所有的item都处于编辑模式下,它的所有的值都可以通过点击一个“Update All”button来更新。见图1。
图 1: 一个完全可编辑的DataList 的所有item都可以被修改
本章我们来学习如何创建一个完全可编辑的DataList,它提供用户更新supplier的address的功能。
第一步: 在DataList的 ItemTemplate创建一个可编辑的用户界面
在前面创建一个标准的item级编辑的DataList时,我们使用了两个template:
ItemTemplate
— 包含只读的用户界面(使用 Label 显示每个product的 name 和price).EditItemTemplate
— 包含编辑的用户界面(两个TextBox ).
DataList的EditItemIndex属性表明了哪个DataListItem使用EditItemTemplate来展示(如果有的话)。即ItemIndex的值等于DataList的EditItemIndex的DataListItem使用EditItemTemplate来展示。在一次只编辑一个item的情况下,这个模式工作的很好,但是在创建完全可编辑的DataList的时候就不适用了。
对完全可编辑的DataList来说,我们需要所有的DataListItem都以可编辑的界面来展示。最简单的方法是在ItemTemplate里定义可编辑的界面。对修改supplier的address信息而言,可编辑界面里supplier表现为文本,address,city和country的值都用TextBox来表示。
首先打开BatchUpdate.aspx页,添加一个DataList,将ID设为Suppliers。通过智能标签添加一个名为SuppliersDataSource的ObjectDataSource控件。
图2: 创建一个名为SuppliersDataSource的ObjectDataSource
使用SuppliersBLL类的GetSuppliers()方法配置ObjectDataSource(见图3)。象前面一章那样,我们将直接使用 BLL而不是通过ObjectDataSource来更新supplier信息。在UPDATE标签里选择None(见图4)。
图 3: 使用GetSuppliers()
方法配置ObjectDataSource
图 4: 设置UPDATE 标签为None
完成向导后,Visual Studio会自动生成DataList的ItemTemplate来在Label里显示每个数据字段。我们需要修改这个template让它提供编辑界面。ItemTemplate可以在设计器里通过DataList的智能标签上的Edit Templates或直接写声明语法来自定义。
创建一个编辑界面,将supplier的name表现为文本,address,city和country表现为TextBox。完成这些后,你的声明代码应该和下面差不多:
ASP.NET | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<asp:DataList ID="Suppliers" runat="server" DataKeyField="SupplierID" DataSourceID="SuppliersDataSource"> <ItemTemplate> <h4><asp:Label ID="CompanyNameLabel" runat="server" Text='<%# Eval("CompanyName") %>'> </asp:Label></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开启view state。
在ItemTemplate里我使用了两个新的CSS类,SupplierPropertyLabel和SupplierPropertyValue。它们的风格设置和ProductsPropertyLabel和ProductPropertyValue CSS类一样,并已经加入到Styles.css中。
CSS | |
1 2 3 4 5 6 7 8 9 10 |
.ProductPropertyLabel, .SupplierPropertyLabel { font-weight: bold; text-align: right; } .ProductPropertyValue, .SupplierPropertyValue { padding-right: 35px; } |
完成这些后浏览页面。如图5所示,每个DataList的item用文本显示supplier name,用TextBox显示address,city和country。
图 5: DataList里的每个Supplier都可编辑
第二步: 增加“Update All” Button
图5里显示的信息暂时还没提供Update按钮。完全可编辑的DataList应该只包含一个"Update All"按钮,而不是象前面那样,每个item包含一个button。当点击"Update All"时,DataList里的所有记录将被更新。本章我们将添加两个"Update All"button- 一个在页的上面,一个在下面(两个都提供相同的功能)。
先在DataList上面添加一个ID为UpdateAll1的Button。然后在DataList下面添加ID为UpdataAll2的Button。两个Button的Text都设为"Update All"。最后为两个Button的Click事件都创建一个event handler。我们创建一个方法,“UpdateAllSupplierAddress”,然后在事件处理中调用它。(而不是在两个事件处理里复制相同的代码)
C# | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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"button后的页面。
图 6: 页面添加了两个“Update All” Button
第三步: 更新所有的Suppliers的 Address 信息
完成了将所有的item显示为可编辑的界面和添加了“Update All”button后,剩下的事就是写代码执行批量更新。我们需要便利DataList的item,调用SuppliersBLL类的UpdateSupplierAddress方法。
可以通过DataList的Items
property 来访问DataListItem集合。通过DataListItem的引用,我们可以从DataKeys集合里获取相关的SuppliserID,并引用ItemTemplate里的TextBox,见下面的代码:
C# | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
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"button时,每个Supplier DataList里的DataListItem都执行UpdateAllSupplierAddress方法,并调用SuppliersBLL类的UpdateSupplierAddress方法,将相关的值传过去。如果address,city或country里不输入值,UpdateSupplierAddress会接收一个空值(不是空字符串),而相关的字段的结果会是一个database NULL。
注意:你可以添加一个显示的状态Label,当批量更新完成后通过它来提供一些确认信息。
只更新 Addresses被修改过的记录
The batch update algorithm used for this tutorial calls the UpdateSupplierAddress
method for every supplier in the DataList, regardless of whether their address information has been changed. While such blind updates aren’t usually a performance issue, they can lead to superfluous records if you’re auditing changes to the database table. For example, if you use triggers to record all UPDATE
s to the Suppliers
table to an auditing table, every time a user clicks the “Update All” button a new audit record will be created for each supplier in the system, regardless of whether the user made any changes.
本章使用的批量更新法则为每个DataList里的supplier调用UpdateSupplierAddress方法,无论address信息是否被修改过。虽然这种盲目的更新一般情况下不会有什么性能问题,但是如果你有做数据库表的审计,那样将会导致很多多余的记录。每次用户点击"Update All"button后,不管用户是否有修改,系统里都会为每个supplier产生一个一条新的审计记录。
ADO.NET的DateTable和DataAdapter类被设计用来支持批量更新那些仅仅被修改,删除或新增的记录。DataTable的每个row都有RowState
property 来指明这个row是否是新增到DataTable或从它里面删除,修改,或没有改变。当DataTable刚产生时,所有的row都被标记为未修改的,修改了row的任何列后row会被标记为已修改的。
在SuppliersBLL类里我们首先将supplier的记录读进SuppliersDataTable里然后设置Address,City和Country列的值来更新指定的supplier的信息,见以下代码:
C# | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
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; } } |
无论值是否有被修改,这段代码都将传入的address,city和country的值赋给SuppliersDataTable的SuppliersRow。这个修改将使SuppliersRow的RowState属性被标记为已修改的。当DAL的Update方法被调用时,它发现SupplierRow已经被修改了,因此向数据库发送UPDATE命令。
然而想象一下,我们为这个方法添加的代码仅仅在和已经存在的值不一样时才将传入的address,city和country的值赋给SuppliersRow。在address,city和country没有修改的情况下,SupplierRow的RowState仍然标记为未改变。这样的结果是当DAL的Update方法被调用时,SuppliersRow没有被修改,因此不会调用数据库。
使用以下的代码代替前面盲目的赋值:
C# | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 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方法仅仅在更改过address相关的值的那些记录里才向数据库发送UPDATE命令。
当然我们也可以追踪传入的字段和数据库数据是否有区别,如果没有,就不需要调用DAL的Update方法。这种方法在你使用直接的数据库命令时非常有效,因为直接的数据库命令不会检查SuppliersRow来决定是否需要调用数据库。
注意:每次UpdateSupplierAddress方法被调用时,都会调用一次数据库来获取需要更新的记录的信息。如果数据被修改,又要调用一次数据库来更新数据。这个流程可以通过创建一个重载的UpdateSupplierAddress方法来优化,这个方法接受一个EmployeesDataTable ,它包含BatchUpdate.aspx页的所有的修改。然后它会调用一次数据库来获取Suppliers表里的所有记录。在结果集里仅仅是被修改过的记录才能被更新。
总结
本章学习了如何创建一个完全可编辑的DataList。通过它用户可以快速的修改多个supplier的address信息。我们首先定义了编辑界面 — address,city和country都以TextBox来表示 — 在DataList的ItemTemplate里。然后我们在DataList的上下各添加了一个“Update All”button。用户修改完后,点击其中一个后,每个DataListItem都会调用SuppliersBLL类的UpdateSupplierAddress方法。