處理 BLL 和 DAL 層級的例外狀況 (C#)

作者:Scott Mitchell

下載 PDF

在本教學課程中,我們將瞭解如何在可編輯的 DataList 更新工作流程期間處理引發的例外狀況。

簡介

DataList 教學課程中編輯和刪除資料的概觀中 ,我們建立了一個提供簡單編輯和刪除功能的 DataList。 雖然功能完全正常,但使用者很難使用,因為編輯或刪除程式期間發生的任何錯誤都會導致未處理的例外狀況。 例如,省略產品名稱,或在編輯產品時輸入價格值為非常便宜!時,會擲回例外狀況。 由於此例外狀況在程序代碼中未攔截,因此會升至 ASP.NET 運行時間,然後顯示網頁中的例外狀況詳細數據。

如我們在 ASP.NET Page 教學課程中處理 BLL 和 DAL-Level 例外 狀況中所見,如果從商業規則或數據存取層的深度引發例外狀況,則會將例外狀況詳細數據傳回至 ObjectDataSource,然後傳回 GridView。 我們已瞭解如何透過建立 Updated ObjectDataSource 或 RowUpdated GridView 的事件處理程式、檢查例外狀況,然後指出已處理例外狀況,以正常處理這些例外狀況。

不過,我們的 DataList 教學課程不會使用 ObjectDataSource 來更新和刪除數據。 相反地,我們會直接處理 BLL。 為了偵測源自 BLL 或 DAL 的例外狀況,我們需要在 ASP.NET 頁面的程式代碼後置內實作例外狀況處理程式代碼。 在本教學課程中,我們將瞭解如何在可編輯的 DataList 更新工作流程期間處理引發的例外狀況。

注意

DataList 教學課程中編輯和刪除資料 的概觀中,我們討論了從 DataList 編輯和刪除數據的不同技術,有些技術涉及使用 ObjectDataSource 進行更新和刪除。 如果您採用這些技術,您可以透過 ObjectDataSource 或UpdatedDeleted事件處理程式來處理 BLL 或 DAL 的例外狀況。

步驟 1:建立可編輯的數據清單

在擔心在更新工作流程期間發生的例外狀況之前,讓我們先建立可編輯的 DataList。 ErrorHandling.aspx開啟資料夾中的頁面EditDeleteDataList、將 DataList 新增至 Designer、將其ID屬性設定為 Products,然後新增名為 ProductsDataSource的新 ObjectDataSource。 將 ObjectDataSource 設定為使用 ProductsBLL 類別 s GetProducts() 方法來選取記錄;將 INSERT、UPDATE 和 DELETE 索引卷標的下拉式清單設定為 [無]) (。

使用 GetProducts () 方法傳回產品資訊

圖 1:使用 GetProducts() 方法傳回產品資訊, (按兩下即可檢視完整大小的影像)

完成 ObjectDataSource 精靈之後,Visual Studio 會自動建立 ItemTemplate DataList 的 。 將此取代為 ItemTemplate ,其中顯示每個產品名稱和價格,並包含 [編輯] 按鈕。 接下來,使用 TextBox Web 控制項來建立 EditItemTemplate ,以取得名稱和價格,以及 [更新] 和 [取消] 按鈕。 最後,將 DataList s RepeatColumns 屬性設定為 2。

這些變更之後,頁面的宣告式標記看起來應該類似下列內容。 重複檢查,確定 [編輯]、[取消] 和 [更新] 按鈕的屬性 CommandName 分別設定為 [編輯]、[取消] 和 [更新]。

<asp:DataList ID="Products" runat="server" DataKeyField="ProductID"
    DataSourceID="ProductsDataSource" RepeatColumns="2">
    <ItemTemplate>
        <h5>
            <asp:Label runat="server" ID="ProductNameLabel"
                Text='<%# Eval("ProductName") %>' />
        </h5>
        Price:
            <asp:Label runat="server" ID="Label1"
                Text='<%# Eval("UnitPrice", "{0:C}") %>' />
        <br />
            <asp:Button runat="server" id="EditProduct" CommandName="Edit"
                Text="Edit" />
        <br />
        <br />
    </ItemTemplate>
    <EditItemTemplate>
        Product name:
            <asp:TextBox ID="ProductName" runat="server"
                Text='<%# Eval("ProductName") %>' />
        <br />
        Price:
            <asp:TextBox ID="UnitPrice" runat="server"
                Text='<%# Eval("UnitPrice", "{0:C}") %>' />
        <br />
        <br />
            <asp:Button ID="UpdateProduct" runat="server" CommandName="Update"
                Text="Update" /> 
            <asp:Button ID="CancelUpdate" runat="server" CommandName="Cancel"
                Text="Cancel" />
    </EditItemTemplate>
</asp:DataList>
<asp:ObjectDataSource ID="ProductsDataSource" runat="server"
    SelectMethod="GetProducts" TypeName="ProductsBLL"
    OldValuesParameterFormatString="original_{0}">
</asp:ObjectDataSource>

注意

在本教學課程中,必須啟用DataList的檢視狀態。

請花點時間檢視瀏覽器的進度, (請參閱圖 2) 。

每個產品都包含 [編輯] 按鈕

圖 2:每個產品都包含 [編輯] 按鈕 (按單擊即可檢視完整大小的影像)

目前,[編輯] 按鈕只會造成回傳尚未讓產品可編輯。 若要啟用編輯,我們需要為 DataList s EditCommandCancelCommandUpdateCommand 事件建立事件處理程式。 EditCommandCancelCommand 事件只會更新 DataList s EditItemIndex 屬性,並將數據重新系結至 DataList:

protected void Products_EditCommand(object source, DataListCommandEventArgs e)
{
    // Set the DataList's EditItemIndex property to the
    // index of the DataListItem that was clicked
    Products.EditItemIndex = e.Item.ItemIndex;
    // Rebind the data to the DataList
    Products.DataBind();
}
protected void Products_CancelCommand(object source, DataListCommandEventArgs e)
{
    // Set the DataList's EditItemIndex property to -1
    Products.EditItemIndex = -1;
    // Rebind the data to the DataList
    Products.DataBind();
}

UpdateCommand事件處理程式會更涉及一些。 它必須從DataKeys集合讀取已編輯的產品ProductID,以及 中 EditItemTemplateTextBoxes 的產品名稱和價格,然後呼叫 ProductsBLL 類別 s UpdateProduct 方法,再將 DataList 傳回其預先編輯狀態。

現在,讓我們在 DataList 中編輯和刪除資料的概觀教學課程中,只使用事件處理程式中的完全相同程序代碼UpdateCommand。 我們將新增程序代碼以正常方式處理步驟 2 中的例外狀況。

protected void Products_UpdateCommand(object source, DataListCommandEventArgs e)
{
    // Read in the ProductID from the DataKeys collection
    int productID = Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]);
    // Read in the product name and price values
    TextBox productName = (TextBox)e.Item.FindControl("ProductName");
    TextBox unitPrice = (TextBox)e.Item.FindControl("UnitPrice");
    string productNameValue = null;
    if (productName.Text.Trim().Length > 0)
        productNameValue = productName.Text.Trim();
    decimal? unitPriceValue = null;
    if (unitPrice.Text.Trim().Length > 0)
        unitPriceValue = Decimal.Parse(unitPrice.Text.Trim(),
            System.Globalization.NumberStyles.Currency);
    // Call the ProductsBLL's UpdateProduct method...
    ProductsBLL productsAPI = new ProductsBLL();
    productsAPI.UpdateProduct(productNameValue, unitPriceValue, productID);
    // Revert the DataList back to its pre-editing state
    Products.EditItemIndex = -1;
    Products.DataBind();
}

面對無效的輸入,其格式可能是格式不正確的單價,可能會引發例外狀況的不合法單價值,例如 -$5.00,或省略產品名稱。 UpdateCommand由於事件處理程式目前未包含任何例外狀況處理程式碼,因此例外狀況會反升至 ASP.NET 運行時間,因此會顯示給使用者 (請參閱圖 3) 。

發生未處理的例外狀況時,使用者會看到錯誤頁面

圖 3:發生未處理的例外狀況時,使用者會看到錯誤頁面

步驟 2:在 UpdateCommand 事件處理程式中正常處理例外狀況

在更新工作流程期間,事件處理程式、BLL 或 DAL 中 UpdateCommand 可能會發生例外狀況。 例如,如果使用者輸入太昂貴的價格, Decimal.Parse 事件處理程式中的 UpdateCommand 語句將會擲回 FormatException 例外狀況。 如果使用者省略產品名稱,或價格有負值,DAL 將會引發例外狀況。

發生例外狀況時,我們想要在頁面本身內顯示資訊訊息。 將標籤 Web 控制項新增至設定ExceptionDetails為 的頁面ID。 將標籤的文字指派給Warning檔案中Styles.css定義的 CSS 類別,將標籤CssClass文字設定為以紅色、超大型、粗體和斜體字型顯示。

發生錯誤時,我們只想要顯示標籤一次。 也就是說,在後續回傳時,標籤警告訊息應該會消失。 這可以藉由清除 Label 的 Text 屬性,或在事件處理程式中Page_Load將其屬性False設定Visible為 (來完成,如同我們在 ASP.NET Page 教學課程中處理 BLL 和 DAL-Level 例外狀況) ,或停用卷標檢視狀態支援。 讓我們使用後者的選項。

<asp:Label ID="ExceptionDetails" EnableViewState="False" CssClass="Warning"
    runat="server" />

引發例外狀況時,我們會將例外狀況的詳細數據指派給 ExceptionDetails Label 控件的 Text 屬性。 由於其檢視狀態已停用,在後續回 Text 傳時,屬性的程式設計變更將會遺失,並還原回默認文字 (空字串) ,藉此隱藏警告訊息。

若要判斷何時引發錯誤,才能在頁面上顯示有用的訊息,我們需要將區塊新增 Try ... CatchUpdateCommand 事件處理程式。 部分 Try 包含可能導致例外狀況的程序代碼,而 Catch 區塊包含在發生例外狀況時執行的程序代碼。 如需區塊的詳細資訊Try ... Catch,請參閱 .NET Framework 檔中的例外狀況處理基本概念一節。

protected void Products_UpdateCommand(object source, DataListCommandEventArgs e)
{
    // Handle any exceptions raised during the editing process
    try
    {
        // Read in the ProductID from the DataKeys collection
        int productID = Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]);
        ... Some code omitted for brevity ...
    }
    catch (Exception ex)
    {
        // TODO: Display information about the exception in ExceptionDetails
    }
}

當區塊內的 Try 程式代碼擲回任何類型的例外狀況時,區塊 Catch 程序代碼就會開始執行。 擲回 DbExceptionNoNullAllowedExceptionArgumentException等等的例外狀況類型取決於第一個位置中的錯誤,確切地推斷錯誤。 如果資料庫層級發生問題, DbException 將會擲回 。 如果針對 UnitPriceUnitsInStockUnitsOnOrderReorderLevel 欄位輸入了不合法的值,將會擲回 , ArgumentException 因為我們已新增程式代碼來驗證類別中的 ProductsDataTable 這些域值, (請參閱 建立商業規則層 教學課程) 。

我們可以藉由根據攔截的例外狀況類型來提供更實用的說明給終端使用者。 下列程式代碼在 處理 BLL 和 DAL-Level Page 教學課程中的處理 BLL 和 DAL-Level 例外狀況 ASP.NET 中 ,以幾乎相同的形式使用,可提供此層級的詳細數據:

private void DisplayExceptionDetails(Exception ex)
{
    // Display a user-friendly message
    ExceptionDetails.Text = "There was a problem updating the product. ";
    if (ex is System.Data.Common.DbException)
        ExceptionDetails.Text += "Our database is currently experiencing problems.
            Please try again later.";
    else if (ex is NoNullAllowedException)
        ExceptionDetails.Text += "There are one or more required fields that are
            missing.";
    else if (ex is ArgumentException)
    {
        string paramName = ((ArgumentException)ex).ParamName;
        ExceptionDetails.Text +=
            string.Concat("The ", paramName, " value is illegal.");
    }
    else if (ex is ApplicationException)
        ExceptionDetails.Text += ex.Message;
}

若要完成本教學課程,只需從Catch傳入攔截Exception實例的 區塊呼叫 DisplayExceptionDetails 方法, (ex) 。

有了 Try ... Catch 區塊,使用者就會看到更具資訊的錯誤訊息,如圖 4 和 5 所示。 請注意,在遇到例外狀況時,DataList 會維持在編輯模式中。 這是因為發生例外狀況之後,控制流程會立即重新導向至 Catch 區塊,略過將 DataList 傳回至其預先編輯狀態的程序代碼。

如果使用者省略必要欄位,就會顯示錯誤訊息

圖 4:如果使用者省略必要的字段 (按兩下即可檢視完整大小的影像 ,就會顯示錯誤訊息)

輸入負價時會顯示錯誤訊息

圖 5:輸入負價時會顯示錯誤訊息, (按兩下即可檢視大小完整的影像)

摘要

GridView 和 ObjectDataSource 提供後置事件處理程式,其中包含更新和刪除工作流程期間所引發之任何例外狀況的相關信息,以及可設定為指出是否已處理例外狀況的屬性。 不過,使用 DataList 並直接使用 BLL 時,無法使用這些功能。 相反地,我們會負責實作例外狀況處理。

在本教學課程中,我們已瞭解如何將區塊新增 Try ... Catch 至事件處理程式,將例外狀況處理新增至 UpdateCommand 可編輯的 DataList 更新工作流程。 如果在更新工作流程期間引發例外狀況,區塊 Catch 程序代碼就會執行,並在標籤中 ExceptionDetails 顯示有用的資訊。

此時,DataList 不會努力防止第一次發生例外狀況。 雖然我們知道負價會導致例外狀況,但我們尚未新增任何功能,以主動防止使用者輸入這類無效的輸入。 在下一個教學課程中,我們將瞭解如何在 中 EditItemTemplate新增驗證控件,協助減少使用者輸入無效所造成的例外狀況。

快樂的程序設計!

深入閱讀

如需本教學課程中討論之主題的詳細資訊,請參閱下列資源:

關於作者

Scott Mitchell 是七份 ASP/ASP.NET 書籍的作者,以及 1998 年以來與 Microsoft Web 技術合作的 4GuysFromRolla.com 作者。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格來連線到 ,您可以在 找到http://ScottOnWriting.NET

特別感謝

本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的首席檢閱者是 Ken Pespisa。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。