Share via


BLL レベルと DAL レベルの例外を処理する (C#)

作成者: Scott Mitchell

PDF のダウンロード

このチュートリアルでは、編集可能な DataList の更新ワークフロー中に発生する例外を適切に処理する方法について説明します。

はじめに

DataList のデータの編集と削除の概要のチュートリアルでは、簡単な編集と削除の機能を提供する DataList を作成しました。 完全に機能する一方で、編集または削除プロセス中に発生したエラーによって未処理の例外が発生しているため、ユーザーフレンドリとは言い難いものでした。 たとえば、製品名を省略したり、製品を編集するときに価格の値を「手頃な価格」と入力すると、例外がスローされます。 この例外はコードでキャッチされないため、ASP.NET ランタイムに吹き出しが表示され、Web ページに例外の詳細が表示されます。

ASP.NET ページで BLL レベルと DAL レベルの例外を処理する」のチュートリアルで説明したとおり、ビジネス ロジックまたはデータ アクセス層の深部から例外が発生した場合、例外の詳細は ObjectDataSource に返され、次に GridView に返されます。 ObjectDataSource または GridView の Updated または RowUpdated イベント ハンドラーを作成し、例外を確認し、例外が処理されたことを示すことによって、これらの例外を正常に処理する方法について説明しました。

ただし、DataList チュートリアルでは、データの更新と削除に ObjectDataSource を使用していません。 代わりに、BLL に対して直接働きかけています。 BLL または DAL から発生した例外を検出するには、ASP.NET ページのコードビハインド内に例外処理コードを実装する必要があります。 このチュートリアルでは、編集可能な DataList の更新ワークフロー中に発生する例外をより適切に処理する方法について説明します。

Note

DataList のデータの編集と削除の概要」のチュートリアルでは、DataList からデータを編集および削除するためのさまざまな手法について説明しました。これには、ObjectDataSource を使用して更新と削除を行う手法がいくつかあります。 これらの手法を採用する場合は、ObjectDataSource の Updated または Deleted イベント ハンドラーを使用して BLL または DAL からの例外を処理できます。

手順 1: 編集可能な DataList の作成

更新ワークフロー中に発生する例外の処理を心配する前に、まずは編集可能な DataList を作成しましょう。 EditDeleteDataList フォルダー内の ErrorHandling.aspx ページを開き、Designer に DataList を追加し、その ID プロパティを Products に設定して、ProductsDataSource という名前の新しい ObjectDataSource を追加します。 レコードを選択するために ProductsBLL クラスの GetProducts() メソッドを使用するように ObjectDataSource を構成します。INSERT タブ、UPDATE タブ、DELETE タブのドロップダウン リストを (None) に設定します。

Return the Product Information Using the GetProducts() Method

図 1: GetProducts() メソッドを使用して製品情報を返す (クリックするとフルサイズの画像が表示されます)

ObjectDataSource ウィザードが完了すると、Visual Studio は DataList 用の ItemTemplate オブジェクトを自動的に作成します。 これを ItemTemplate に置き換えると、各製品の名前と価格が表示され、[編集] ボタンが表示されます。 次に、名前と価格の TextBox Web コントロールと、更新ボタンとキャンセル ボタンを持つ EditItemTemplate を作成します。 最後に、DataList の RepeatColumns プロパティを 2 に設定します。

これらの変更後、GridView の宣言型マークアップは次のようになります。 [編集]、[キャンセル]、[更新] ボタンの 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>

Note

このチュートリアルでは、DataList のビュー状態を有効にする必要があります。

ブラウザー経由で進行状況を確認します (図 2 参照)。

Each Product Includes an Edit Button

図 2: [編集] ボタンを含む各製品 (クリックするとフルサイズの画像が表示されます)

現在、[編集] ボタンはポストバックを行うだけで、製品を編集可能にすることはありません。 編集を可能にするには、DataListの EditCommandCancelCommand、および UpdateCommand イベント用のイベント ハンドラーを作成する必要があります。 EditCommand および CancelCommand イベントは、単純に DataList の 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 イベント ハンドラーはもう少し複雑です。 編集された製品の ProductIDDataKeys コレクションから EditItemTemplate の TextBox から製品の名前および価格と共に読み込み、DataList を編集前の状態に戻す前に ProductsBLL クラスの UpdateProduct メソッドを呼び出します。

ここでは、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 参照)。

When an Unhandled Exception Occurs, the End User Sees an Error Page

図 3: 未処理の例外が発生した場合にエンド ユーザーに表示されたエラー ページ

手順 2: UpdateCommand イベント ハンドラーでの正常な例外の処理

更新ワークフロー中に、UpdateCommand イベント ハンドラー、BLL、または DAL で例外が発生する可能性があります。 たとえば、ユーザが [高すぎる] 価格を入力した場合、UpdateCommand イベント ハンドラーの Decimal.Parse ステートメントは FormatException 例外をスローします。 ユーザーが製品名を省略したり、価格が負の値である場合、DAL で例外が発生します。

例外が発生した場合は、ページ自体に情報メッセージが表示されます。 IDExceptionDetails に設定されているページに Label Web コントロールを追加します。 その CssClass プロパティを Styles.css ファイルで定義されている Warning CSS クラスに割り当てて、ラベルのテキストを赤、特大、太字、斜体で表示するように構成します。

エラーが発生した場合は、ラベルを 1 回だけ表示します。 つまり、その後のポストバックでは、ラベルの警告メッセージは表示されなくなります。 これは、ラベルの Text プロパティをクリアするか、Page_Load イベント ハンドラーでその Visible プロパティを False に設定するか (チュートリアル「ASP.NET ページで BLL レベルと DAL レベルの例外を処理する」で行ったように)、ラベルのビュー状態のサポートを無効にすることで実現できます。 後者のオプションを使用してみましょう。

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

例外が発生したら、例外の詳細を ExceptionDetails Label コントロールの Text プロパティに割り当てます。 そのビュー状態は無効であるため、その後のポストバックでは Text プロパティのプログラムによる変更は失われ、既定のテキスト (空の文字列) に戻り、警告メッセージは非表示になります。

ページに役立つメッセージを表示するために、エラーが発生したタイミングを判断するには、Try ... Catch ブロックを UpdateCommand イベント ハンドラーに追加する必要があります。 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 クラスにこれらのフィールド値を検証するコードを追加したためです (チュートリアル「ビジネス ロジック層を作成する」参照)。

キャッチされた例外の種類に基づいてメッセージ テキストを基にすることで、エンド ユーザーにさらに役立つ説明を提供できます。 チュートリアル「ASP.NET ページで BLL レベルと DAL レベルの例外を処理する」でほぼ同じ形式で使用した次のコードでは、次のレベルの詳細が提供されます。

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 ブロックから DisplayExceptionDetails メソッドを呼び出し、キャッチした Exception インスタンス (ex) を渡すだけです。

Try ... Catch ブロックを配置すると、図 4 と図 5 に示すように、ユーザーには詳細なエラー メッセージが表示されます。 例外が発生しても DataList は編集モードのままであることに注意してください。 これは、例外が発生すると、制御フローが直ちに Catch ブロックにリダイレクトされ、DataList を編集前の状態に戻すコードがバイパスされるためです。

An Error Message is Displayed if a User Omits a Required Field

図 4: ユーザーが必須フィールドを省略した場合に表示されるエラー メッセージ (クリックするとフルサイズの画像が表示されます)

An Error Message is Displayed When Entering a Negative Price

図 5: 負の価格を入力した場合に表示されるエラー メッセージ (クリックするとフルサイズの画像が表示されます)

まとめ

GridView と ObjectDataSource には、更新と削除のワークフロー中に発生した例外に関する情報と、例外が処理されたかどうかを示すために設定できるプロパティを含む、ポスト レベルのイベント ハンドラーが用意されています。 ただし、これらの機能は、DataList を操作して BLL を直接使用する場合は使用できません。 代わりに、例外処理を実装する責任があります。

このチュートリアルでは、Try ... Catch ブロックを UpdateCommand イベント ハンドラーに追加することによって、編集可能な DataList の更新ワークフローに例外処理を追加する方法について説明しました。 更新ワークフロー中に例外が発生した場合、Catch ブロックのコードが実行され、ExceptionDetails ラベルに役立つ情報が表示されます。

この時点で、DataList では例外の発生を未然に防ぐ取り組みは行われていません。 マイナスの価格を入力すると例外が発生することがわかっていても、ユーザーがそのような無効な入力をするのを未然に防ぐ機能はまだ追加されていません。 次のチュートリアルでは、EditItemTemplate に検証コントロールを追加することで、無効なユーザー入力によって例外が発生しにくくする方法について説明します。

プログラミングに満足!

もっと読む

この記事で説明したトピックの詳細については、次のリソースを参照してください。

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを扱っています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Ken Pespisas でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。