執行批次更新 (C#)
瞭解如何建立完全可編輯的 DataList,其中其所有專案都在編輯模式中,而且其值可以透過按兩下頁面上的 [全部更新] 按鈕來儲存。
簡介
在 上述教學課程中, 我們檢查了如何建立專案層級 DataList。 就像標準可編輯的 GridView 一樣,DataList 中的每個項目都會包含 [編輯] 按鈕,當單擊時,專案可編輯。 雖然此專案層級編輯適用於偶爾只會更新的數據,但某些使用案例需要使用者編輯許多記錄。 如果使用者需要編輯數十筆記錄,並強制按兩下 [編輯]、進行變更,然後按兩下每筆記錄的 [更新],則按兩下次數可能會降低其生產力。 在這種情況下,更好的選項是提供完全可編輯的 DataList,其中 其所有 專案都處於編輯模式,且其值可藉由按下頁面上的 [全部更新] 按鈕來編輯, (請參閱圖 1) 。
圖 1:按兩下即可修改完全可編輯 DataList 中的每個專案, (按兩下即可檢視完整大小的影像)
在本教學課程中,我們將探討如何讓使用者使用完全可編輯的 DataList 來更新供應商地址資訊。
步驟 1:在 DataList s ItemTemplate 中建立可編輯的使用者介面
在上述教學課程中,我們會在其中建立標準、專案層級可編輯的 DataList,我們使用兩個範本:
ItemTemplate
包含唯讀使用者介面, (標籤 Web 控件,以顯示每個產品名稱和價格) 。EditItemTemplate
包含編輯模式使用者介面, (兩個 TextBox Web 控件) 。
DataList 的 EditItemIndex
屬性會 DataListItem
指定如果使用 EditItemTemplate
轉譯任何) , (。 特別是, DataListItem
其 ItemIndex
值符合 DataList s EditItemIndex
屬性的 會使用 EditItemTemplate
轉譯。 此模型在一次只能編輯一個項目時運作良好,但在建立可完全編輯的 DataList 時會分開。
針對完全可編輯的 DataList,我們想要使用可編輯的介面轉譯 所有DataListItem
。 若要達成此目的,最簡單的方式是在 中 ItemTemplate
定義可編輯的介面。 若要修改供應商地址資訊,可編輯的介面會包含供應商名稱做為文字,然後包含位址、城市和國家/地區值的 TextBoxes。
從開啟 BatchUpdate.aspx
頁面開始,新增 DataList 控制項,並將其 屬性設定 ID
為 Suppliers
。 從 DataList 智慧標記中,選擇新增名為 SuppliersDataSource
的新 ObjectDataSource 控制件。
圖 2:建立名為 SuppliersDataSource
的新 ObjectDataSource (按兩下即可檢視完整大小的影像)
設定 ObjectDataSource 以使用 SuppliersBLL
類別 s GetSuppliers()
方法來擷取數據 (請參閱圖 3) 。 如同上述教學課程,我們不會透過 ObjectDataSource 更新供應商資訊,而是直接使用商業規則層。 因此,將下拉式清單設定為 ([更新] 索引卷標中的 [無) ], (請參閱圖 4) 。
圖 3:使用 GetSuppliers()
方法擷取供貨商資訊 (按一下以檢視完整大小的影像)
圖 4:在 [更新] 索引標籤中,將 [Drop-Down 列表] 設定為 [無 (]) , (按兩下即可檢視完整大小的影像)
完成精靈之後,Visual Studio 會自動產生 DataList , ItemTemplate
以顯示卷標 Web 控件中數據源傳回的每個數據欄位。 我們需要修改此範本,以便改為提供編輯介面。 ItemTemplate
可以使用 DataList 智慧標記中的 [編輯範本] 選項,或直接透過宣告式語法,透過 Designer 自定義 。
請花點時間建立編輯介面,以文字顯示供應商的名稱,但包含供應商位址、城市和國家/地區值的 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 類別和 SupplierPropertyValue
,SupplierPropertyLabel
這些類別已新增至 Styles.css
類別,並設定為使用與 和 ProductPropertyValue
CSS 類別相同的樣式設定ProductPropertyLabel
。
.ProductPropertyLabel, .SupplierPropertyLabel
{
font-weight: bold;
text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
padding-right: 35px;
}
進行這些變更之後,請透過瀏覽器瀏覽此頁面。 如圖 5 所示,每個 DataList 項目都會以文字顯示供應商名稱,並使用 TextBox 來顯示位址、城市和國家/地區。
圖 5:D ataList 中的每個供應商都可以編輯 (按兩下即可檢視全大小的影像)
步驟 2:新增 [全部更新] 按鈕
雖然圖 5 中的每個供應商都有其位址、城市和國家/地區字段顯示在 TextBox 中,但目前沒有可用的 [更新] 按鈕。 相較於每個專案都有一個 [更新] 按鈕,且具有完全可編輯的DataList,頁面上通常會有單一 [全部更新] 按鈕,按兩下時, 就會更新 DataList中的所有記錄。 在本教學課程中,讓我們新增兩個 [全部更新] 按鈕 - 一個位於頁面頂端,另一個位於底部 (,不過按兩下任一按鈕的效果會相同) 。
首先,在 DataList 上方新增 Button Web 控制件,並將其 ID
屬性設定為 UpdateAll1
。 接下來,在 DataList 底下新增第二個 Button Web 控件,並將其 ID
設定為 UpdateAll2
。 Text
將兩個按鈕的屬性設定為 [全部更新]。 最後,建立這兩個 Buttons Click
事件的事件處理程式。 讓我們將該邏輯重構為第三個方法,讓事件處理程式只叫用這個第三個方法 UpdateAllSupplierAddresses
,而不是複製每個事件處理程式中的更新邏輯。
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 顯示新增 [全部更新] 按鈕之後的頁面。
圖 6:兩個更新所有按鈕都已新增至頁面 (按兩下即可檢視完整大小的影像)
步驟 3:更新所有供應商地址資訊
所有 DataList 專案都會顯示編輯介面,並新增 [全部更新] 按鈕,而保留的所有專案都是撰寫程式代碼來執行批次更新。 具體而言,我們需要循環處理 DataList 的專案,併為每個專案呼叫 SuppliersBLL
類別 s UpdateSupplierAddress
方法。
可透過 DataList 的 屬性存取 DataList 的Items
實例集合DataListItem
。 透過 的參考DataListItem
,我們可以從 DataKeys
集合抓取對應的 SupplierID
,並以程式設計方式參考 中的 ItemTemplate
TextBox 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);
}
}
當使用者按兩下其中一個 [全部更新] 按鈕時, UpdateAllSupplierAddresses
方法會逐 DataListItem
一查看DataList中的每個 Suppliers
按鈕,並呼叫 SuppliersBLL
類別 s UpdateSupplierAddress
方法,並傳入對應的值。 位址、城市或國家/地區傳遞 Nothing
的非輸入值為 (UpdateSupplierAddress
,而不是空白字元串) ,這會導致 NULL
基礎記錄字段的資料庫。
注意
做為增強功能,您可能會想要將狀態標籤 Web 控件新增至頁面,以在執行批次更新之後提供一些確認訊息。
只更新已修改的位址
本教學課程所使用的批次更新演算法會UpdateSupplierAddress
針對 DataList 中的每個供應商呼叫 方法,而不論其地址資訊是否已變更。 雖然這類盲目更新通常不是效能問題,但當您重新稽核資料庫數據表的變更時,可能會導致多餘的記錄。 例如,如果您使用觸發程式將所有記錄 UPDATE
到 Suppliers
稽核數據表,每當使用者按兩下 [全部更新] 按鈕時,系統中的每個供應商都會建立新的稽核記錄,而不論使用者是否進行任何變更。
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;
}
}
不論值是否已變更,此程式代碼都會將傳入的位址、城市和國家/地區值指派給 SuppliersRow
中的 SuppliersDataTable
。 這些修改會導致 SuppliersRow
屬性 RowState
標示為已修改。 呼叫數據存取層的 Update
方法時,會看到 SupplierRow
已修改 ,因此會將命令傳送 UPDATE
至資料庫。
不過,想像一下,我們已將程式代碼新增至這個方法,只有在傳入的位址、城市和國家/地區值與現有值不同 SuppliersRow
時,才指派傳入的位址、城市和國家/地區值。 如果位址、城市和國家/地區與現有資料相同,則不會進行任何 SupplierRow
變更,且會 RowState
保留標示為未變更的 。 淨結果是呼叫 DAL s 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 s Update
方法只會針對位址相關值已變更的記錄,將語句傳送 UPDATE
至資料庫。
或者,我們可以追蹤傳入的位址字段與資料庫數據之間是否有任何差異,如果沒有,只要略過 DAL s 方法的 Update
呼叫即可。 如果您使用 DB 直接方法,這個方法就很適合使用,因為 DB 直接方法並未傳遞 SuppliersRow
可檢查的實例 RowState
,以判斷是否真的需要資料庫呼叫。
注意
每次叫用 方法時 UpdateSupplierAddress
,都會呼叫資料庫以擷取已更新記錄的相關信息。 然後,如果數據有任何變更,則會對資料庫進行另一個呼叫來更新數據表數據列。 建立方法多載,以接受EmployeesDataTable
具有頁面所有變更BatchUpdate.aspx
的實例,藉此優化UpdateSupplierAddress
此工作流程。 然後,它可以對資料庫進行一次呼叫,以從 Suppliers
數據表取得所有記錄。 然後可以列舉這兩個結果集,而且只有發生變更的記錄可以更新。
摘要
在本教學課程中,我們已瞭解如何建立完全可編輯的 DataList,讓使用者快速修改多個供應商的地址資訊。 我們一開始是在 DataList s ItemTemplate
中為供應商位址、城市和國家/地區值定義 TextBox Web 控制件的編輯介面。 接下來,我們新增了 DataList 上方和下方的 [更新所有] 按鈕。 用戶進行變更並按兩下其中一個 [全部更新] 按鈕之後, DataListItem
會列舉 s,並 SuppliersBLL
呼叫 類別 s UpdateSupplierAddress
方法。
快樂的程序設計!
關於作者
Scott Mitchell 是七份 ASP/ASP.NET 書籍的作者,以及 自 1998 年以來與 Microsoft Web 技術合作的 4GuysFromRolla.com 作者。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格來連線到 ,您可以在 找到http://ScottOnWriting.NET。
特別感謝
本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的首席檢閱者是 Zack Jones 和 Ken Pespisa。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。