執行批次更新 (VB)
瞭解如何建立完全可編輯的 DataList,其中其所有專案都在編輯模式中,且其值可以藉由按兩下頁面上的 [全部更新] 按鈕來儲存。
簡介
在上述教學課程中,我們已檢查如何建立專案層級 DataList。 就像標準可編輯的 GridView 一樣,DataList 中的每個專案都包含 [編輯] 按鈕,當單擊時,專案可編輯。 雖然此專案層級的編輯適用於只偶爾更新的數據,但某些使用案例需要使用者編輯許多記錄。 如果使用者需要編輯數十筆記錄,並被迫按兩下 [編輯],進行變更,然後按兩下 [更新],則按兩下次數可能會妨礙她的生產力。 在這種情況下,更好的選項是提供完全可編輯的 DataList,其中 所有 專案都在編輯模式中,而其值可以透過按兩下頁面上的 [全部更新] 按鈕來編輯(請參閱圖 1)。
圖 1:可以修改完整編輯 DataList 中的每個專案(按兩下以檢視完整大小的影像)
在本教學課程中,我們將探討如何讓使用者使用完全可編輯的 DataList 來更新供應商地址資訊。
步驟 1:在 DataList s ItemTemplate 中建立可編輯的使用者介面
在上述教學課程中,我們會在其中建立標準、專案層級可編輯的 DataList,我們使用兩個範本:
ItemTemplate
包含唯讀使用者介面(用於顯示每個產品名稱和價格的標籤 Web 控制項)。EditItemTemplate
包含編輯模式使用者介面(兩個 TextBox Web 控制件)。
DataList 的 EditItemIndex
屬性會指定使用 EditItemTemplate
轉譯的內容DataListItem
(如果有的話)。 特別是, DataListItem
其 ItemIndex
值符合 DataList 屬性的 EditItemIndex
會使用 EditItemTemplate
來轉譯。 此模型在一次只能編輯一個項目時運作良好,但在建立可完全編輯的 DataList 時會分崩離析。
對於完全可編輯的 DataList,我們希望 所有 DataListItem
的 都使用可編輯的介面來轉譯。 若要達成此目的,最簡單的方式是在 中 ItemTemplate
定義可編輯的介面。 若要修改供應商地址資訊,可編輯的介面包含供應商名稱做為文字,然後是位址、城市和國家/地區值的 TextBoxes。
從開啟 BatchUpdate.aspx
頁面開始,新增 DataList 控制項,並將其 屬性設定 ID
為 Suppliers
。 從 DataList 的智慧標記中,選擇新增名為 SuppliersDataSource
的新 ObjectDataSource 控件。
圖 2:建立一個名為 SuppliersDataSource
的新 ObjectDataSource (點擊查看完整圖片)
設定 ObjectDataSource 以使用 SuppliersBLL
類別 s GetSuppliers()
方法擷取數據(請參閱圖 3)。 如同上述教學課程,我們不會透過 ObjectDataSource 更新供應商資訊,而是直接使用商業規則層。 因此,將 [UPDATE] 索引標籤中的下拉式清單設定為 [無] (請參閱圖 4)。
圖 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 必須啟用其檢視狀態。
在 I ItemTemplate
中使用兩個新的 CSS 類別和 SupplierPropertyValue
,SupplierPropertyLabel
這些類別已新增至 Styles.css
類別,並設定為使用 與和 ProductPropertyValue
CSS 類別相同的樣式設定ProductPropertyLabel
。
.ProductPropertyLabel, .SupplierPropertyLabel
{
font-weight: bold;
text-align: right;
}
.ProductPropertyValue, .SupplierPropertyValue
{
padding-right: 35px;
}
進行這些變更後,透過瀏覽器造訪此頁面。 如圖 5 所示,每個 DataList 項目都會以文字顯示供應商名稱,並使用 TextBoxes 來顯示位址、城市和國家/地區。
圖 5:D ataList 中的每個供應商都是可編輯的(按兩下以檢視完整大小的影像)
步驟 2:新增 [更新所有] 按鈕
雖然圖 5 中的每個供應商都有其位址、城市和國家/地區字段顯示在 TextBox 中,但目前沒有可用的 [更新] 按鈕。 除了每個專案都有一個 [更新] 按鈕,而且具有完全可編輯的DataLists,通常頁面上有一個[全部更新] 按鈕,按兩下時,會更新 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 的Items
實例集合DataListItem
。 使用 的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
類別 s UpdateSupplierAddress
方法,並傳入對應的值。 位址、城市或國家/地區傳遞的非輸入值是 Nothing
的值( UpdateSupplierAddress
而不是空白字串),這會導致 NULL
基礎記錄字段的資料庫。
注意
為了增強功能,您可能會想要將狀態標籤 Web 控件新增至頁面,以在執行批次更新之後提供一些確認訊息。
只更新已修改的位址
本教學課程所使用的批次更新演算法會針對 DataList 中的每個供應商呼叫 UpdateSupplierAddress
方法,不論其地址資訊是否已變更。 雖然這種盲目更新通常不是效能問題,但如果您正在審核對資料庫表的更改,它們可能會導致多餘的記錄。 例如,如果您使用觸發程式將所有 記錄 UPDATE
到數據表到 Suppliers
稽核數據表,則每次用戶按兩下 [全部更新] 按鈕時,系統中的每個供應商都會建立新的稽核記錄,而不論使用者是否進行任何變更。
ADO.NET DataTable 和 DataAdapter 類別的設計目的是支援批次更新,其中只有修改、刪除和新記錄會導致任何資料庫通訊。 DataTable 中的每個數據列都有一個 RowState
屬性 ,指出數據列是否已新增至 DataTable、從其中刪除、修改或保持不變。 一開始填入 DataTable 時,所有數據列都會標示為未變更。 變更任何數據列數據列的值,會將數據列標示為已修改。
在類別中 SuppliersBLL
,我們會先將單一供應商記錄中讀取為 SuppliersDataTable
,然後使用下列程式代碼來設定 Address
、 City
和 Country
資料行值,以更新指定的供應商地址資訊:
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
現有值不同時,才會指派傳入的位址、城市和國家/地區值。 如果位址、城市和國家/地區與現有資料相同,則不會進行任何變更,且會 SupplierRow
保留標示為未變更的 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 的 Update
方法只會針對地址相關值已變更的記錄,將語句傳送 UPDATE
至資料庫。
或者,我們可以追蹤傳入的位址字段與資料庫數據之間是否有任何差異,如果沒有,只要略過 DAL 方法的 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 書籍的作者和 4GuysFromRolla.com 創始人,自 1998 年以來便開始使用 Microsoft Web 技術。 Scott 擔任獨立顧問、講師和作家。 他的新書是 Sams Teach Yourself ASP.NET 2.0 in 24 Hours。 您可以透過 mitchell@4GuysFromRolla.com 或他的部落格 (可以在 http://ScottOnWriting.NET 找到) 與他聯繫。
特別感謝
本教學課程系列已經過許多熱心的檢閱者檢閱。 本教學課程的主要檢閱者是 Zack Jones 和 Ken Pespisa。 有興趣檢閱我即將推出的 MSDN 文章嗎? 如果有,請發信到 mitchell@4GuysFromRolla.com 。