ASP.NET ページで BLL レベルと DAL レベルの例外を処理する (VB)
このチュートリアルでは、ASP.NET データ Web コントロールの挿入、更新、または削除操作中に例外が発生した場合に、わかりやすいわかりやすいエラー メッセージを表示する方法について説明します。
はじめに
階層化されたアプリケーション アーキテクチャを使用して ASP.NET Web アプリケーションのデータを操作するには、次の 3 つの一般的な手順が必要です。
- ビジネス ロジック レイヤーの呼び出しに必要なメソッドと、それを渡すパラメーター値を決定します。 パラメーター値は、ハードコーディング、プログラムによる割り当て、またはユーザーが入力した入力にすることができます。
- メソッドを呼び出します。
- 結果を処理します。 データを返す BLL メソッドを呼び出すときは、データをデータ Web コントロールにバインドする必要があります。 データを変更する BLL メソッドの場合、これには、戻り値に基づいて何らかのアクションを実行したり、手順 2 で発生した例外を適切に処理したりすることが含まれます。
前のチュートリアルで説明したように、ObjectDataSource コントロールとデータ Web コントロールの両方に、手順 1 と 3 の機能拡張ポイントが用意されています。 たとえば、GridView は、ObjectDataSource UpdateParameters
のRowUpdating
コレクションにフィールド値を割り当てる前にイベントを発生させます。そのRowUpdated
イベントは、ObjectDataSource が操作を完了した後に発生します。
手順 1 で発生するイベントを既に調べ、入力パラメーターのカスタマイズや操作の取り消しにどのように使用できるかを確認しました。 このチュートリアルでは、操作の完了後に発生するイベントに注意を向けます。 これらの事後レベルのイベント ハンドラーを使用すると、特に、操作中に例外が発生したかどうかを判断し、正常に処理し、標準の ASP.NET 例外ページに既定ではなく、わかりやすい有益なエラー メッセージを画面に表示できます。
これらの事後レベルのイベントの操作を説明するために、編集可能な GridView 内の製品を一覧表示するページを作成しましょう。 製品を更新するときに、例外が発生した場合、ASP.NET ページには、問題が発生したことを説明する短いメッセージが GridView の上に表示されます。 それでは作業を始めましょう。
手順 1: 編集可能な GridView の製品の作成
前のチュートリアルでは、 と の 2 つのフィールド ProductName
のみを含む編集可能な GridView を作成しました UnitPrice
。 これには、各製品フィールドのUpdateProduct
パラメーターではなく、3 つの入力パラメーター (製品名、単価、ID) のみを受け入れたクラスのメソッドに対して追加のオーバーロードProductsBLL
を作成する必要がありました。 このチュートリアルでは、この手法をもう一度練習して、製品名、単位あたりの数量、単価、在庫単位を表示する編集可能な GridView を作成します。ただし、名前、単価、在庫単位のみを編集できます。
このシナリオに対応するには、 メソッドの別の UpdateProduct
オーバーロードが必要です。1 つは、製品の名前、単価、在庫単位、ID の 4 つのパラメーターを受け入れます。 次のメソッドを ProductsBLL
クラスに追加します。
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct _
(ByVal productName As String, ByVal unitPrice As Nullable(Of Decimal), _
ByVal unitsInStock As Nullable(Of Short), ByVal productID As Integer) As Boolean
Dim products As Northwind.ProductsDataTable = _
Adapter.GetProductByProductID(productID)
If products.Count = 0 Then
Return False
End If
Dim product As Northwind.ProductsRow = products(0)
product.ProductName = productName
If Not unitPrice.HasValue Then
product.SetUnitPriceNull()
Else
product.UnitPrice = unitPrice.Value
End If
If Not unitsInStock.HasValue Then
product.SetUnitsInStockNull()
Else
product.UnitsInStock = unitsInStock.Value
End If
Dim rowsAffected As Integer = Adapter.Update(product)
Return rowsAffected = 1
End Function
このメソッドが完了したら、これら 4 つの特定の製品フィールドを編集できる ASP.NET ページを作成する準備ができました。 フォルダー内のErrorHandling.aspx
ページをEditInsertDelete
開き、Designerを使用してページに GridView を追加します。 GridView を新しい ObjectDataSource にバインドし、メソッドをSelect()
クラスのGetProducts()
メソッドにProductsBLL
、メソッドをUpdate()
先ほど作成したUpdateProduct
オーバーロードにマッピングします。
図 1: 4 つの入力パラメーターを UpdateProduct
受け入れるメソッド オーバーロードを使用する (フルサイズの画像を表示する 場合は、ここをクリックします)
これにより、4 つのパラメーターを UpdateParameters
持つコレクションと、各製品フィールドのフィールドを含む GridView を含む ObjectDataSource が作成されます。 ObjectDataSource の宣言型マークアップは、プロパティに OldValuesParameterFormatString
値 original_{0}
を割り当てます。これにより、BLL クラスでは という名前 original_productID
の入力パラメーターが渡されるとは想定されないため、例外が発生します。 宣言構文からこの設定を完全に削除することを忘れないでください (または既定値 {0}
に設定してください)。
次に、GridView を減らして、、QuantityPerUnit
、UnitPrice
および UnitsInStock
BoundFields のみをProductName
含めます。 また、必要と思われるフィールド レベルの書式設定 (プロパティの変更など) も自由に HeaderText
適用できます。
前のチュートリアルでは、BoundField を読み取り専用モードと編集モードの両方で通貨として書式設定 UnitPrice
する方法について説明しました。 ここで同じ操作を行います。 図 2 に示すように、BoundField の DataFormatString
プロパティを に {0:c}
設定し、その HtmlEncode
プロパティを に false
、その ApplyFormatInEditMode
プロパティを に true
設定する必要があることを思い出してください。
図 2: BoundField を UnitPrice
通貨として表示するように構成する (フルサイズの画像を表示する場合はクリックします)
UnitPrice
編集インターフェイスで を通貨として書式設定するには、通貨形式の文字列を値に解析する GridView のイベントのRowUpdating
イベント ハンドラーを作成するdecimal
必要があります。 最後のチュートリアルの RowUpdating
イベント ハンドラーもチェックして、ユーザーが値を指定したことを確認したことを思い UnitPrice
出してください。 ただし、このチュートリアルでは、ユーザーが価格を省略できるようにしましょう。
Protected Sub GridView1_RowUpdating(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) _
Handles GridView1.RowUpdating
If e.NewValues("UnitPrice") IsNot Nothing Then
e.NewValues("UnitPrice") = _
Decimal.Parse(e.NewValues("UnitPrice").ToString(), _
System.Globalization.NumberStyles.Currency)
End If
GridView には BoundField が含まれています QuantityPerUnit
が、この BoundField は表示目的でのみ使用し、ユーザーが編集することはできません。 これを配置するには、BoundFields の ReadOnly
プロパティを に true
設定するだけです。
図 3: BoundField を QuantityPerUnit
Read-Only する (フルサイズの画像を表示するをクリックします)
最後に、GridView のスマート タグから [編集を有効にする] チェック ボックスをチェックします。 これらの手順を完了すると、ErrorHandling.aspx
ページのDesignerは図 4 のようになります。
図 4: 必要な BoundFields を除くすべてを削除し、[編集を有効にする] チェック ボックスをオンにします (フルサイズの画像を表示するにはクリックします)
この時点で、すべての製品の ProductName
、QuantityPerUnit
UnitPrice
および UnitsInStock
フィールドの一覧が表示されますが、編集できるのは 、UnitPrice
、 UnitsInStock
フィールドのみですProductName
。
図 5: ユーザーはストック フィールドで製品の名前、価格、ユニットを簡単に編集できるようになりました (フルサイズの画像を表示する をクリックします)
手順 2: DAL-Level 例外を適切に処理する
編集可能な GridView は、ユーザーが編集した製品の名前、価格、および単位の有効な値を在庫に入力すると素晴らしく機能しますが、無効な値を入力すると例外が発生します。 たとえば、値をProductName
省略すると、クラスAllowDBNull
の ProductsRow
プロパティのプロパティが に設定false
されているためProductName
、NoNullAllowedException がスローされます。データベースがダウンしている場合、SqlException
データベースに接続しようとすると TableAdapter によって がスローされます。 これらの例外は、何も行わずに、データ アクセス層からビジネス ロジック層にバブル アップし、次に [ASP.NET] ページに、最後に ASP.NET ランタイムにバブル アップします。
Web アプリケーションの構成方法と、 から localhost
アプリケーションにアクセスするかどうかに応じて、ハンドルされない例外によって、一般的なサーバー エラー ページ、詳細なエラー レポート、またはユーザー フレンドリな Web ページが発生する可能性があります。 ASP.NET ランタイムがキャッチされない例外に応答する方法の詳細については、「 ASP.NET の Web アプリケーション エラー処理 」と 「customErrors 要素 」を参照してください。
図 6 は、値を指定せずに製品を更新しようとしたときに発生した画面を ProductName
示しています。 これは、 を経由 localhost
したときに表示される既定の詳細なエラー レポートです。
図 6: 製品名を省略すると例外の詳細が表示されます (フルサイズの画像を表示する場合はクリックします)
このような例外の詳細はアプリケーションをテストするときに役立ちますが、例外が発生した場合にエンド ユーザーにこのような画面を表示することは理想的ではありません。 エンド ユーザーは、 が何であるか NoNullAllowedException
、なぜ原因が発生したのかを知らない可能性があります。 より良い方法は、製品の更新を試みる際に問題が発生したことを説明するわかりやすいメッセージをユーザーに表示することです。
操作の実行時に例外が発生した場合、ObjectDataSource とデータ Web コントロールの両方のポストレベル イベントは、それを検出し、例外が ASP.NET ランタイムにバブルアップするのを取り消す手段を提供します。 この例では、GridView RowUpdated
のイベントのイベント ハンドラーを作成して、例外が発生したかどうかを判断し、発生した場合は Label Web コントロールに例外の詳細を表示します。
まず、ASP.NET ページに Label を追加し、そのプロパティを ID
に ExceptionDetails
設定し、そのプロパティを Text
クリアします。 このメッセージにユーザーの目を引くには、その CssClass
プロパティを に Warning
設定します。これは、前のチュートリアルでファイルに Styles.css
追加した CSS クラスです。 この CSS クラスにより、ラベルのテキストが赤、斜体、太字、特大フォントで表示されることを思い出してください。
図 7: ラベル Web コントロールをページに追加する (フルサイズの画像を表示する場合はクリックします)
この Label Web コントロールは例外が発生した直後にのみ表示されるようにするため、イベント ハンドラーでそのPage_Load
プロパティを false に設定Visible
します。
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
ExceptionDetails.Visible = False
End Sub
このコードでは、最初のページの visit とそれ以降のポストバックで、 ExceptionDetails
コントロールの Visible
プロパティが に false
設定されます。 GRIDView のイベント ハンドラーで検出できる DAL レベルまたは BLL レベルのRowUpdated
例外が発生した場合は、コントロールの Visible
プロパティを ExceptionDetails
true に設定します。 Web コントロール のイベント ハンドラーはページ ライフサイクルのイベント ハンドラーの Page_Load
後に発生するため、Label が表示されます。 ただし、次のポストバックでは、Page_Load
イベント ハンドラーによって プロパティが にfalse
戻Visible
され、再び非表示になります。
注意
または、宣言構文でプロパティを割り当て、そのビューステートを無効にすることで、コントロールのVisible
プロパティfalse
を Visible
にPage_Load
設定ExceptionDetails
する必要を取り除く方法があります (そのEnableViewState
プロパティを にfalse
設定します)。 この代替アプローチは、今後のチュートリアルで使用します。
Label コントロールを追加した次の手順では、GridView RowUpdated
のイベントのイベント ハンドラーを作成します。 Designerで GridView を選択し、プロパティ ウィンドウに移動し、稲妻アイコンをクリックして GridView のイベントを一覧表示します。 このチュートリアルの前半でこのイベントのイベント ハンドラーを作成したので、GridView RowUpdating
のイベント用のエントリが既に存在する必要があります。 イベントのイベント ハンドラー RowUpdated
も作成します。
図 8: GridView のイベントのイベント ハンドラーを RowUpdated
作成する
注意
分離コード クラス ファイルの上部にあるドロップダウン リストを使用して、イベント ハンドラーを作成することもできます。 左側のドロップダウン リストから GridView を選択し、 RowUpdated
右側からイベントを選択します。
このイベント ハンドラーを作成すると、ASP.NET ページの分離コード クラスに次のコードが追加されます。
Protected Sub GridView1_RowUpdated(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.GridViewUpdatedEventArgs) _
Handles GridView1.RowUpdated
End Sub
このイベント ハンドラーの 2 番目の入力パラメーターは、 GridViewUpdatedEventArgs 型のオブジェクトであり、例外を処理するための 3 つのプロパティがあります。
Exception
スローされた例外への参照。例外がスローされていない場合、このプロパティの値は になります。null
ExceptionHandled
イベント ハンドラーでRowUpdated
例外が処理されたかどうかを示すブール値。(既定値) の場合false
、例外が再スローされ、ASP.NET ランタイムまでパーコレートされます。KeepInEditMode
編集した GridView 行にtrue
設定されている場合は編集モードのままです。(既定値) の場合false
、GridView 行は読み取り専用モードに戻ります
このコードでは、 が ではないnull
かどうかをException
確認チェック必要があります。これは、操作の実行中に例外が発生したことを意味します。 この場合は、次の操作を行います。
- ラベルにわかりやすいメッセージを表示する
ExceptionDetails
- 例外が処理されたことを示す
- GridView 行を編集モードのままにする
次のコードは、これらの目標を達成します。
Protected Sub GridView1_RowUpdated(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.GridViewUpdatedEventArgs) _
Handles GridView1.RowUpdated
If e.Exception IsNot Nothing Then
ExceptionDetails.Visible = True
ExceptionDetails.Text = "There was a problem updating the product. "
If e.Exception.InnerException IsNot Nothing Then
Dim inner As Exception = e.Exception.InnerException
If TypeOf inner Is System.Data.Common.DbException Then
ExceptionDetails.Text &= _
"Our database is currently experiencing problems." & _
"Please try again later."
ElseIf TypeOf inner _
Is System.Data.NoNullAllowedException Then
ExceptionDetails.Text += _
"There are one or more required fields that are missing."
ElseIf TypeOf inner Is ArgumentException Then
Dim paramName As String = CType(inner, ArgumentException).ParamName
ExceptionDetails.Text &= _
String.Concat("The ", paramName, " value is illegal.")
ElseIf TypeOf inner Is ApplicationException Then
ExceptionDetails.Text += inner.Message
End If
End If
e.ExceptionHandled = True
e.KeepInEditMode = True
End If
End Sub
このイベント ハンドラーは、 が null
かどうかをe.Exception
確認することから始まります。 そうでない場合、Label Visible
の プロパティは にtrue
設定され、ExceptionDetails
そのText
プロパティは "製品の更新中に問題が発生しました" に設定されます。スローされた実際の例外の詳細は、 e.Exception
オブジェクトの InnerException
プロパティにあります。 この内部例外が調べられ、特定の型の場合は、Label Text
の プロパティにExceptionDetails
追加の有用なメッセージが追加されます。 最後に、 ExceptionHandled
プロパティと KeepInEditMode
プロパティの両方が に true
設定されます。
図 9 は、製品の名前を省略したときのこのページのスクリーン ショットを示しています。図 10 は、無効な UnitPrice
値 (-50) を入力した場合の結果を示しています。
図 9: BoundField には ProductName
値を含める必要があります (フルサイズの画像を表示する場合はクリックします)
図 10: 負の UnitPrice
値は許可されていません (フルサイズの画像を表示する場合はクリックします)
プロパティを e.ExceptionHandled
に true
設定すると、 RowUpdated
イベント ハンドラーによって例外が処理されたことが示されます。 そのため、例外は ASP.NET ランタイムに反映されません。
注意
図 9 と図 10 は、無効なユーザー入力によって発生した例外を適切に処理する方法を示しています。 ただし、このような無効な入力は、最初はビジネス ロジック レイヤーに到達しないのが理想的です。ASP.NET ページでは、クラスUpdateProduct
のメソッドを呼び出す前にユーザーの入力が有効であることを確認するProductsBLL
必要があります。 次のチュートリアルでは、ビジネス ロジック レイヤーに送信されたデータがビジネス ルールに準拠していることを確認するために、編集インターフェイスと挿入インターフェイスに検証コントロールを追加する方法について説明します。 検証コントロールは、ユーザーが指定したデータが有効になるまでメソッドの UpdateProduct
呼び出しを防ぐだけでなく、データ入力の問題を特定するためのより有益なユーザー エクスペリエンスを提供します。
手順 3: BLL-Level 例外を適切に処理する
データを挿入、更新、または削除するときに、データ アクセス層は、データ関連のエラーが発生したときに例外をスローする可能性があります。 データベースがオフラインであるか、必要なデータベース テーブル列に値が指定されていないか、テーブル レベルの制約に違反している可能性があります。 厳密にデータ関連の例外に加えて、ビジネス ロジック レイヤーでは例外を使用して、ビジネス ルールに違反したタイミングを示すことができます。 たとえば、ビジネス ロジック レイヤーの作成チュートリアルでは、ビジネス ルール チェックを元UpdateProduct
のオーバーロードに追加しました。 具体的には、ユーザーが製品を廃止としてマークしていた場合、その製品がサプライヤーによって提供される唯一の製品ではないことを要求しました。 この条件に違反した場合は、 ApplicationException
がスローされました。
このチュートリアルで作成したUpdateProduct
オーバーロードについて、フィールドが元UnitPrice
の値の 2 倍以上の新しい値に設定されることを禁止UnitPrice
するビジネス ルールを追加しましょう。 これを実現するには、このチェックを実行し、ルールに違反した場合に をApplicationException
スローするようにオーバーロードを調整UpdateProduct
します。 更新されたメソッドは次のとおりです。
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct(ByVal productName As String, _
ByVal unitPrice As Nullable(Of Decimal), ByVal unitsInStock As Nullable(Of Short), _
ByVal productID As Integer) As Boolean
Dim products As Northwind.ProductsDataTable = Adapter.GetProductByProductID(productID)
If products.Count = 0 Then
Return False
End If
Dim product As Northwind.ProductsRow = products(0)
If unitPrice.HasValue AndAlso Not product.IsUnitPriceNull() Then
If unitPrice > product.UnitPrice * 2 Then
Throw New ApplicationException( _
"When updating a product price," & _
" the new price cannot exceed twice the original price.")
End If
End If
product.ProductName = productName
If Not unitPrice.HasValue Then
product.SetUnitPriceNull()
Else
product.UnitPrice = unitPrice.Value
End If
If Not unitsInStock.HasValue Then
product.SetUnitsInStockNull()
Else
product.UnitsInStock = unitsInStock.Value
End If
Dim rowsAffected As Integer = Adapter.Update(product)
Return rowsAffected = 1
End Function
この変更により、既存の価格の 2 倍を超える価格更新が発生すると ApplicationException
、 がスローされます。 DAL から発生した例外と同様に、この BLL 発生 ApplicationException
は GridView RowUpdated
のイベント ハンドラーで検出および処理できます。 実際、イベント ハンドラーのRowUpdated
コードは、記述されているように、この例外を正しく検出し、 の Message
プロパティ値をApplicationException
表示します。 図 11 は、ユーザーが Chai の価格を 50.00 ドル (現在の価格の 19.95 ドルの 2 倍以上) に更新しようとしたときのスクリーン ショットを示しています。
図 11: ビジネス ルールでは、製品の価格の 2 倍を超える価格上昇を禁止します (フルサイズの画像を表示する をクリックします)
注意
ビジネス ロジック ルールは、メソッドの UpdateProduct
オーバーロードから一般的なメソッドにリファクタリングされるのが理想的です。 これは、読者の演習として残されます。
まとめ
挿入、更新、および削除操作中に、データ Web コントロールと ObjectDataSource の両方が、実際の操作を予約する事前および事後レベルのイベントを発生させます。 このチュートリアルと前のチュートリアルで説明したように、編集可能な GridView を操作すると、GridView の RowUpdating
イベントが発生し、その後に ObjectDataSource の Updating
イベントが発生し、その後に更新コマンドが ObjectDataSource の基になるオブジェクトに対して行われます。 操作が完了すると、ObjectDataSource の Updated
イベントが発生し、その後に GridView の RowUpdated
イベントが発生します。
入力パラメーターをカスタマイズするために、または操作の結果を検査して応答するために、事前レベルのイベントのイベント ハンドラーを作成できます。 ポストレベルのイベント ハンドラーは、操作中に例外が発生したかどうかを検出するために最も一般的に使用されます。 例外が発生した場合、これらの事後レベルのイベント ハンドラーは、必要に応じて例外を独自に処理できます。 このチュートリアルでは、わかりやすいエラー メッセージを表示して、このような例外を処理する方法について説明しました。
次のチュートリアルでは、データの書式設定の問題 (負 UnitPrice
の値を入力するなど) によって発生する例外の可能性を減らす方法について説明します。 具体的には、編集インターフェイスと挿入インターフェイスに検証コントロールを追加する方法について説明します。
プログラミングに満足!
著者について
7 冊の ASP/ASP.NET 書籍の著者であり、 4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジと協力しています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は Liz Shulok でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示