批次刪除 (C#)

由 Scott Mitchell

下載 PDF

了解如何在單一操作中刪除多個資料庫記錄。 在使用者介面層中,我們將根據之前教學課程中建立的增強型 GridView 進行擴展。 在資料存取層中,我們將多個刪除操作包裝在一個交易中,以確保所有刪除成功或所有刪除都會回滾。

簡介

先前的教學課程探討如何使用完全可編輯的 GridView 建立批次編輯介面。 在使用者經常同時編輯多個記錄的情況下,批次編輯介面將需要的回傳和鍵盤到滑鼠上下文切換的次數更少,從而提高終端使用者的效率。 這種技術對於使用者經常一次性刪除多筆記錄的頁面同樣非常有用。

使用過線上電子郵件用戶端的任何人都已經熟悉最常見的批次刪除介面之一:網格中每行都有一個核取方塊,並帶有相應的「刪除所有選取的項目」按鈕 (見圖 1)。 這個教學課程相對較短,因為我們在之前的教學課程中已經完成了所有辛苦的工作,包括建立基於網頁的介面和實現一次性刪除多筆記錄的方法。 在「新增 GridView 核取方塊欄」教學課程中,我們建立了一個含有核取方塊欄的 GridView。而在「將資料庫修改包裝在交易中」教學課程中,我們在 BLL 中建立了一個方法,該方法將會使用交易來刪除 List 的 ProductID 值。 在本教學課程中,我們將整合之前的經驗,建立一個可工作的批次刪除範例。

每行都包含一個核取方塊

圖 1:每行包含一個核取方塊 (點此查看全尺寸圖片)

步驟 1:建立批次刪除介面

由於我們在「新增 GridView 核取方塊列」教學中已經建立了批次刪除介面,因此我們可以將其直接複製到,而不必從頭開始建立。 首先,打開 BatchDelete.aspx 資料夾中的 BatchData 頁面和 CheckBoxField.aspx 資料夾中的 EnhancedGridView 頁面。 從 CheckBoxField.aspx 頁面轉到「原始檢視」,並複製 <asp:Content> 標籤之間的標記,如圖 2 所示。

將 CheckBoxField.aspx 的宣告標記複製到剪貼簿

圖 2:將 的宣告式標記複製到剪貼簿 (點擊查看完整圖片)

接下來,前往 BatchDelete.aspx 中的來源檢視,並將剪貼簿的內容貼到 <asp:Content> 標籤內。 另外,將 CheckBoxField.aspx.cs 中的程式碼複製並貼上到 BatchDelete.aspx.cs 中的程式碼隱藏類別中(DeleteSelectedProducts 按鈕的 Click 事件處理常式、ToggleCheckState 方法,以及 Click 和 CheckAll 按鈕的 UncheckAll 事件處理常式)。 複製此內容後,BatchDelete.aspx 頁面的後置程式碼類別應包含以下程式碼:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class BatchData_BatchDelete : System.Web.UI.Page
{
    protected void DeleteSelectedProducts_Click(object sender, EventArgs e)
    {
        bool atLeastOneRowDeleted = false;
        // Iterate through the Products.Rows property
        foreach (GridViewRow row in Products.Rows)
        {
            // Access the CheckBox
            CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
            if (cb != null && cb.Checked)
            {
                // Delete row! (Well, not really...)
                atLeastOneRowDeleted = true;
                // First, get the ProductID for the selected row
                int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
                // "Delete" the row
                DeleteResults.Text += string.Format
                    ("This would have deleted ProductID {0}<br />", productID);
                //... To actually delete the product, use ...
                //ProductsBLL productAPI = new ProductsBLL();
                //productAPI.DeleteProduct(productID);
                //............................................
            }
        }
        // Show the Label if at least one row was deleted...
        DeleteResults.Visible = atLeastOneRowDeleted;
    }
    private void ToggleCheckState(bool checkState)
    {
        // Iterate through the Products.Rows property
        foreach (GridViewRow row in Products.Rows)
        {
            // Access the CheckBox
            CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
            if (cb != null)
                cb.Checked = checkState;
        }
    }
    protected void CheckAll_Click(object sender, EventArgs e)
    {
        ToggleCheckState(true);
    }
    protected void UncheckAll_Click(object sender, EventArgs e)
    {
        ToggleCheckState(false);
    }
}

複製宣告性標記和原始程式碼後,花點時間透過瀏覽器查看以測試BatchDelete.aspx。 您應該會看到一個 GridView 列出了 GridView 中的前十個產品,每行都列出了產品的名稱、類別和價格以及一個核取方塊。 應有三個按鈕:全部選取、全部取消選取、刪除所選產品。 按一下「全部選取」按鈕將選取所有核取方塊,而「取消全部選取」將清除所有核取方塊。 按下「刪除所選產品」會顯示一則訊息,其中列出了所選產品的 ProductID 值,但實際上並沒有刪除產品。

CheckBoxField.aspx 的介面已移至 BatchDeleting.aspx

圖 3: 的介面已移至 (點擊查看完整圖片)

步驟 2:使用交易刪除選取的產品

將批次刪除介面成功複製到 BatchDeleting.aspx 後,剩下的就是更新程式碼,以便「刪除所選產品」按鈕使用 DeleteProductsWithTransaction 方法來刪除選取的產品,此方法位於 ProductsBLL 類別中。 此方法介紹於「在交易中包裝資料庫修改」教學課程中,採用 List 格式的 ProductID 值作為輸入,並刪除交易範圍內每個對應的 ProductID。

DeleteSelectedProducts 按鈕的 Click 事件處理常式目前使用以下 foreach 迴圈來迭代每個 GridView 列:

// Iterate through the Products.Rows property
foreach (GridViewRow row in Products.Rows)
{
    // Access the CheckBox
    CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
    if (cb != null && cb.Checked)
    {
        // Delete row! (Well, not really...)
        atLeastOneRowDeleted = true;
        // First, get the ProductID for the selected row
        int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
        // "Delete" the row
        DeleteResults.Text += string.Format
            ("This would have deleted ProductID {0}<br />", productID);
        //... To actually delete the product, use ...
        //ProductsBLL productAPI = new ProductsBLL();
        //productAPI.DeleteProduct(productID);
        //............................................
    }
}

對於每一行,會以程式設計方式參考 ProductSelector CheckBox Web 控制項。 如果勾選,則會從 DataKeys 集合中擷取行 ProductID,並更新 DeleteResults 標籤的 Text 屬性以包含指示該行已選擇刪除的訊息。

上述的程式碼實際上並沒有刪除任何記錄,因為對 ProductsBLL 類別的 Delete 方法的呼叫已被註解掉。如果套用此刪除邏輯,程式碼將刪除產品,但不會以單一原子操作的方式進行刪除。 也就是說,如果序列中的前幾次刪除成功,但後來的幾次刪除失敗 (可能由於違反外部索引鍵限制),則會擲回例外狀況,但已刪除的那些產品將保持刪除狀態。

為了確保原子性,我們需要使用 ProductsBLL 類別的 DeleteProductsWithTransaction 方法。 因為此方法接受 ProductID 值清單,所以我們需要先從網格匯集此清單,然後將其作為參數傳遞。 我們會先建立一個List<T>型別的int實例。 在 foreach 迴圈中,我們需要將所選產品的 ProductID 值新增至此 List<T>。 循環之後,此 List<T> 必須傳遞到 ProductsBLL 類別的 DeleteProductsWithTransaction 方法。 使用以下程式碼更新 DeleteSelectedProducts 按鈕的 Click 事件處理常式:

protected void DeleteSelectedProducts_Click(object sender, EventArgs e)
{
    // Create a List to hold the ProductID values to delete
    System.Collections.Generic.List<int> productIDsToDelete = 
        new System.Collections.Generic.List<int>();
    // Iterate through the Products.Rows property
    foreach (GridViewRow row in Products.Rows)
    {
        // Access the CheckBox
        CheckBox cb = (CheckBox)row.FindControl("ProductSelector");
        if (cb != null && cb.Checked)
        {
            // Save the ProductID value for deletion
            // First, get the ProductID for the selected row
            int productID = Convert.ToInt32(Products.DataKeys[row.RowIndex].Value);
            // Add it to the List...
            productIDsToDelete.Add(productID);
            // Add a confirmation message
            DeleteResults.Text += string.Format
                ("ProductID {0} has been deleted<br />", productID);
        }
    }
    // Call the DeleteProductsWithTransaction method and show the Label 
    // if at least one row was deleted...
    if (productIDsToDelete.Count > 0)
    {
        ProductsBLL productAPI = new ProductsBLL();
        productAPI.DeleteProductsWithTransaction(productIDsToDelete);
        DeleteResults.Visible = true;
        // Rebind the data to the GridView
        Products.DataBind();
    }
}

更新的程式碼建立 List<T> 類型的 int (productIDsToDelete),並用要刪除的 ProductID 值填入。 在 foreach 迴圈結束後,如果至少選擇了一個產品,則呼叫 ProductsBLL 類別的 DeleteProductsWithTransaction 方法並傳遞此清單。 DeleteResults 標籤也會顯示,並且資料會重新繫結到 GridView(這樣新刪除的紀錄就不再在網格中顯示為行)。

圖 4 顯示了選擇刪除多行後的 GridView。 圖 5 顯示了點擊「刪除所選產品」按鈕後立即出現的畫面。 請注意,在圖 5 中,已刪除記錄的ProductID值顯示在 GridView 下方的標籤中,而那些行已不再出現在 GridView 中。

將刪除所選產品

圖 4:將刪除所選產品(按此以查看完整圖片)

刪除產品的 ProductID 值列在 GridView 之下顯示。

圖 5:已刪除產品的數值列在 GridView 下方 (點擊查看完整圖片)

注意

若要測試 DeleteProductsWithTransaction 方法的原子性,請手動在 Order Details 表格中新增產品項目,然後嘗試刪除該產品(連同其他產品)。 嘗試刪除具有關聯訂單的產品時,您將收到外部索引鍵約束衝突,但請注意其他選定產品的刪除是如何回溯的。

摘要

建立批次刪除介面涉及新增一個帶有核取方塊列的 GridView 和一個 Button Web 控制項,當按一下該控制項時,將作為單一原子操作刪除所有選取的行。 在本教學課程中,我們通過將先前兩個教學課程中完成的工作結合起來,建立了一個介面,這兩個課程分別是「新增 GridView 核取方塊的欄」和「將資料庫修改包裹在交易中」。 在第一個教學課程中,我們建立了一個包含核取方塊的一欄的 GridView。在後者中,我們在 BLL (商業邏輯層) 中實作了一個方法,當傳遞一系列的 ProductID 值時,會在交易的範圍內將這些值全部刪除。

在下一個教學課程中,我們將建立一個用於執行批次插入的介面。

快樂程式!

關於作者

斯科特·米切爾,七本 ASP/ASP.NET 書籍和 4GuysFromRolla.com 創始人的作者,自1998年以來一直與Microsoft Web 技術合作。 Scott 是一位獨立顧問、培訓師兼作家。 他的最新著作是《Sams Teach Yourself ASP.NET 2.0 in 24 Hours》。 可以透過 mitchell@4GuysFromRolla.com 聯絡他。

特別感謝

本教學課程系列已經過許多熱心的審閱者檢閱。 本教學課程的主要審閱者是 Hilton Giesenow 和 Teresa Murphy。 有興趣查看我即將發表的 MSDN 文章嗎? 如果是,請在 mitchell@4GuysFromRolla.com給我留言。

上一個 下一個