執行批次更新 (C#)

作者 :Scott Mitchell

下載 PDF

瞭解如何建立完全可編輯的 DataList,其中其所有專案都在編輯模式中,而且其值可以透過按兩下頁面上的 [全部更新] 按鈕來儲存。

簡介

上述教學課程中, 我們檢查了如何建立專案層級 DataList。 就像標準可編輯的 GridView 一樣,DataList 中的每個項目都會包含 [編輯] 按鈕,當單擊時,專案可編輯。 雖然此專案層級編輯適用於偶爾只會更新的數據,但某些使用案例需要使用者編輯許多記錄。 如果使用者需要編輯數十筆記錄,並強制按兩下 [編輯]、進行變更,然後按兩下每筆記錄的 [更新],則按兩下次數可能會降低其生產力。 在這種情況下,更好的選項是提供完全可編輯的 DataList,其中 其所有 專案都處於編輯模式,且其值可藉由按下頁面上的 [全部更新] 按鈕來編輯, (請參閱圖 1) 。

您可以修改完全可編輯 DataList 中的每個專案

圖 1:按兩下即可修改完全可編輯 DataList 中的每個專案, (按兩下即可檢視完整大小的影像)

在本教學課程中,我們將探討如何讓使用者使用完全可編輯的 DataList 來更新供應商地址資訊。

步驟 1:在 DataList s ItemTemplate 中建立可編輯的使用者介面

在上述教學課程中,我們會在其中建立標準、專案層級可編輯的 DataList,我們使用兩個範本:

  • ItemTemplate 包含唯讀使用者介面, (標籤 Web 控件,以顯示每個產品名稱和價格) 。
  • EditItemTemplate 包含編輯模式使用者介面, (兩個 TextBox Web 控件) 。

DataList 的 EditItemIndex 屬性會 DataListItem 指定如果使用 EditItemTemplate轉譯任何) , (。 特別是, DataListItemItemIndex 值符合 DataList s EditItemIndex 屬性的 會使用 EditItemTemplate轉譯。 此模型在一次只能編輯一個項目時運作良好,但在建立可完全編輯的 DataList 時會分開。

針對完全可編輯的 DataList,我們想要使用可編輯的介面轉譯 所有DataListItem 。 若要達成此目的,最簡單的方式是在 中 ItemTemplate定義可編輯的介面。 若要修改供應商地址資訊,可編輯的介面會包含供應商名稱做為文字,然後包含位址、城市和國家/地區值的 TextBoxes。

從開啟 BatchUpdate.aspx 頁面開始,新增 DataList 控制項,並將其 屬性設定 IDSuppliers。 從 DataList 智慧標記中,選擇新增名為 SuppliersDataSource的新 ObjectDataSource 控制件。

建立名為 SuppliersDataSource 的新 ObjectDataSource

圖 2:建立名為 SuppliersDataSource 的新 ObjectDataSource (按兩下即可檢視完整大小的影像)

設定 ObjectDataSource 以使用 SuppliersBLL 類別 s GetSuppliers() 方法來擷取數據 (請參閱圖 3) 。 如同上述教學課程,我們不會透過 ObjectDataSource 更新供應商資訊,而是直接使用商業規則層。 因此,將下拉式清單設定為 ([更新] 索引卷標中的 [無) ], (請參閱圖 4) 。

使用 GetSuppliers () 方法擷取供應商資訊

圖 3:使用 GetSuppliers() 方法擷取供貨商資訊 (按一下以檢視完整大小的影像)

將 [Drop-Down 列表] 設定為 [更新] 索引標籤中的 [無) ] (

圖 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 類別和 SupplierPropertyValueSupplierPropertyLabel這些類別已新增至 Styles.css 類別,並設定為使用與 和 ProductPropertyValue CSS 類別相同的樣式設定ProductPropertyLabel

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

進行這些變更之後,請透過瀏覽器瀏覽此頁面。 如圖 5 所示,每個 DataList 項目都會以文字顯示供應商名稱,並使用 TextBox 來顯示位址、城市和國家/地區。

DataList 中的每個供應商都是可編輯的

圖 5:D ataList 中的每個供應商都可以編輯 (按兩下即可檢視全大小的影像)

步驟 2:新增 [全部更新] 按鈕

雖然圖 5 中的每個供應商都有其位址、城市和國家/地區字段顯示在 TextBox 中,但目前沒有可用的 [更新] 按鈕。 相較於每個專案都有一個 [更新] 按鈕,且具有完全可編輯的DataList,頁面上通常會有單一 [全部更新] 按鈕,按兩下時, 就會更新 DataList中的所有記錄。 在本教學課程中,讓我們新增兩個 [全部更新] 按鈕 - 一個位於頁面頂端,另一個位於底部 (,不過按兩下任一按鈕的效果會相同) 。

首先,在 DataList 上方新增 Button Web 控制件,並將其 ID 屬性設定為 UpdateAll1。 接下來,在 DataList 底下新增第二個 Button Web 控件,並將其 ID 設定為 UpdateAll2Text將兩個按鈕的屬性設定為 [全部更新]。 最後,建立這兩個 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 中的每個供應商呼叫 方法,而不論其地址資訊是否已變更。 雖然這類盲目更新通常不是效能問題,但當您重新稽核資料庫數據表的變更時,可能會導致多餘的記錄。 例如,如果您使用觸發程式將所有記錄 UPDATESuppliers 稽核數據表,每當使用者按兩下 [全部更新] 按鈕時,系統中的每個供應商都會建立新的稽核記錄,而不論使用者是否進行任何變更。

ADO.NET DataTable 和 DataAdapter 類別的設計目的是支援批次更新,其中只有修改、刪除和新記錄會導致任何資料庫通訊。 DataTable 中的每個數據列都有屬性RowState,指出數據列是否已加入 DataTable、從其中刪除、修改或維持不變。 一開始填入 DataTable 時,所有數據列都會標示為未變更。 變更任何數據列數據列的值,會將數據列標示為已修改。

在類別中 SuppliersBLL ,我們會先在單一供應商記錄中讀取 , SuppliersDataTable 然後使用下列程式代碼來設定 AddressCityCountry 資料行值,以更新指定的供應商地址資訊:

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放在 。