ビジネス ロジック層を作成する (VB)
このチュートリアルでは、プレゼンテーション層と DAL 間のデータ交換の仲介役となるビジネス ロジック層 (BLL) にビジネス ルールを一元化する方法について説明します。
はじめに
最初のチュートリアルで作成されたデータ アクセス層 (DAL) は、データ アクセス ロジックとプレゼンテーション ロジックを完全に分離します。 ただし、DAL は、データ アクセスの詳細をプレゼンテーション層から完全に分離しますが、適用される可能性のあるビジネス ルールは適用しません。 たとえば、今回のアプリケーションの場合、Discontinued
フィールドが 1 に設定されている場合に Products
テーブルの CategoryID
フィールドまたは SupplierID
フィールドを変更できないようにしたり、年功序列規則を適用して、従業員が後から採用された人によって管理される状況を禁止したい場合があります。 もう 1 つの一般的なシナリオは、特定のロールのユーザーのみが製品を削除したり、UnitPrice
値を変更したりできる承認です。
このチュートリアルでは、プレゼンテーション層と DAL 間のデータ交換の仲介役となるビジネス ロジック層 (BLL) にこれらのビジネス ルールを一元化する方法について説明します。 実際のアプリケーションでは、BLL を個別のクラス ライブラリ プロジェクトとして実装する必要があります。ただし、今回のチュートリアルでは、プロジェクト構造を簡略化するために、App_Code
フォルダー内の一連のクラスとして BLL を実装します。 図 1 は、プレゼンテーション層、BLL、DAL 間のアーキテクチャの関係を示しています。
図 1: BLL はプレゼンテーション層をデータ アクセス層から分離し、ビジネス ルールを課す
ビジネス ロジックを実装するために個別のクラスを作成するのではなく、部分クラスを持つ型指定されたデータセットにこのロジックを直接配置することもできます。 型指定されたデータセットの作成と拡張の例については、最初のチュートリアルを参照してください。
手順 1: BLL クラスの作成
今回の BLL は、DAL の TableAdapter ごとに 1 つずつの、4 つのクラスで構成されます。これらの各 BLL クラスには、DAL 内のそれぞれの TableAdapter から取得、挿入、更新、削除を行い、適切なビジネス ルールを適用するためのメソッドが用意されています。
DAL 関連のクラスと BLL 関連のクラスをより完全に分離するために、App_Code
フォルダーに、DAL
と BLL
の 2 つのサブフォルダーを作成します。 ソリューション エクスプローラーで App_Code
フォルダーを右クリックし、[新しいフォルダー] を選択するだけです。 これら 2 つのフォルダーを作成した後、最初のチュートリアルで作成した型指定されたデータセットを DAL
サブフォルダーに移動します。
次に、BLL
サブフォルダーに 4 つの BLL クラス ファイルを作成します。 これを行うには、BLL
サブフォルダーを右クリックし、[新しい項目の追加] を選択して、クラス テンプレートを選択します。 4 つのクラスに ProductsBLL
、CategoriesBLL
、SuppliersBLL
、EmployeesBLL
と名前を付けます。
図 2: App_Code
フォルダーに 4 つの新しいクラスを追加する
次に、各クラスにメソッドを追加して、最初のチュートリアルの TableAdapters に対して定義されたメソッドをラップします。 現時点では、これらのメソッドは DAL に直接呼び出すだけです。必要なビジネス ロジックを追加するために後で戻ります。
Note
Visual Studio Standard Edition 以上を使用している場合 (つまり、Visual Web Developer を使用していない場合)、必要に応じてクラス デザイナーを使用してクラスを視覚的に設計できます。 Visual Studio のこの新機能の詳細については、クラス デザイナーのブログを参照してください。
ProductsBLL
クラスには、次の合計 7 つのメソッドを追加する必要があります。
GetProducts()
: すべての製品を返しますGetProductByProductID(productID)
: 指定した製品 ID を持つ製品を返しますGetProductsByCategoryID(categoryID)
: 指定したカテゴリのすべての製品を返しますGetProductsBySupplier(supplierID)
: 指定したサプライヤーのすべての製品を返しますAddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)
: 渡された値を使用して新しい製品をデータベースに挿入します。新しく挿入されたレコードのProductID
値を返しますUpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)
: 渡された値を使用してデータベース内の既存の製品を更新します。1 行が更新された場合はTrue
を返します。それ以外の場合はFalse
を返しますDeleteProduct(productID)
: 指定した製品をデータベースから削除します
ProductsBLL.vb
Imports NorthwindTableAdapters
<System.ComponentModel.DataObject()> _
Public Class ProductsBLL
Private _productsAdapter As ProductsTableAdapter = Nothing
Protected ReadOnly Property Adapter() As ProductsTableAdapter
Get
If _productsAdapter Is Nothing Then
_productsAdapter = New ProductsTableAdapter()
End If
Return _productsAdapter
End Get
End Property
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, True)> _
Public Function GetProducts() As Northwind.ProductsDataTable
Return Adapter.GetProducts()
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductByProductID(ByVal productID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductByProductID(productID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsByCategoryID(ByVal categoryID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsByCategoryID(categoryID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsBySupplierID(ByVal supplierID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsBySupplierID(supplierID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Insert, True)> _
Public Function AddProduct( _
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean) _
As Boolean
Dim products As New Northwind.ProductsDataTable()
Dim product As Northwind.ProductsRow = products.NewProductsRow()
product.ProductName = productName
If Not supplierID.HasValue Then
product.SetSupplierIDNull()
Else
product.SupplierID = supplierID.Value
End If
If Not categoryID.HasValue Then
product.SetCategoryIDNull()
Else
product.CategoryID = categoryID.Value
End If
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
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
If Not unitsOnOrder.HasValue Then
product.SetUnitsOnOrderNull()
Else
product.UnitsOnOrder = unitsOnOrder.Value
End If
If Not reorderLevel.HasValue Then
product.SetReorderLevelNull()
Else
product.ReorderLevel = reorderLevel.Value
End If
product.Discontinued = discontinued
products.AddProductsRow(product)
Dim rowsAffected As Integer = Adapter.Update(products)
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct(_
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean, 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 supplierID.HasValue Then
product.SetSupplierIDNull()
Else
product.SupplierID = supplierID.Value
End If
If Not categoryID.HasValue Then
product.SetCategoryIDNull()
Else
product.CategoryID = categoryID.Value
End If
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
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
If Not unitsOnOrder.HasValue Then
product.SetUnitsOnOrderNull()
Else
product.UnitsOnOrder = unitsOnOrder.Value
End If
If Not reorderLevel.HasValue Then
product.SetReorderLevelNull()
Else
product.ReorderLevel = reorderLevel.Value
End If
product.Discontinued = discontinued
Dim rowsAffected As Integer = Adapter.Update(product)
Return rowsAffected = 1
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Delete, True)> _
Public Function DeleteProduct(ByVal productID As Integer) As Boolean
Dim rowsAffected As Integer = Adapter.Delete(productID)
Return rowsAffected = 1
End Function
End Class
単にデータ GetProducts
、GetProductByProductID
、GetProductsByCategoryID
、GetProductBySuppliersID
を返すメソッドは、DAL に呼び出すだけなので、非常に単純です。 一部のシナリオでは、このレベルで実装する必要があるビジネス ルール (現在ログオンしているユーザーやユーザーが属するロールに基づく認可規則など) が存在する場合がありますが、これらのメソッドはそのままにしておきます。 これらのメソッドの場合、BLL は、プレゼンテーション層が基になるデータ アクセス層からのデータにアクセスする際に経由する、単なるプロキシとして機能します。
AddProduct
メソッドと UpdateProduct
メソッドの両方が、さまざまな製品フィールドの値をパラメーターとして受け取り、新しい製品を追加するか、既存の製品をそれぞれ更新します。 Product
テーブルの列の多くは NULL
値を受け取ることができるので (いくつか例をあげると、CategoryID
、SupplierID
、UnitPrice
など)、そのような列に対応する AddProduct
や UpdateProduct
の入力パラメーターは null 許容型を使用します。 .NET 2.0 で新しく導入された null 許容型は、値の型が、代わりに、Nothing
である必要があるかどうかを示す手法を提供します。 詳しくは、Paul Vick による Null 許容型と VB に関する真実に関するブログ エントリと、Null 許容構造体の技術ドキュメントを参照してください。
3 つのメソッドはすべて、行が挿入、更新、または削除されたかどうかを示すブール値を返します。これは、操作が影響を受ける行にならない可能性があるためです。 たとえば、ページ開発者が存在しない製品に対して ProductID
を渡す DeleteProduct
呼び出しを行った場合、データベースに対して発行された DELETE
ステートメントは影響を受けないため、DeleteProduct
メソッドから False
が返されます。
新しい製品を追加したり、既存のものを更新したりする場合、ProductsRow
インスタンスを受け入れるのではなく、新規または変更された製品のフィールド値をスカラーのリストとして取り込むことに注意してください。 この方法は、ProductsRow
クラスが既定のパラメーターなしのコンストラクターを持たない ADO.NET DataRow
クラスから派生しているために選択されました。 新しい ProductsRow
インスタンスを作成するには、最初に ProductsDataTable
インスタンスを作成してから、その NewProductRow()
メソッドを呼び出す必要があります (これは AddProduct
で行います)。 この欠点は、ObjectDataSource を使用して製品の挿入と更新を行うときに表面化します。 つまり、ObjectDataSource は入力パラメーターのインスタンスの作成を試みます。 BLL メソッドで ProductsRow
インスタンスが必要な場合、ObjectDataSource はインスタンスの作成を試みますが、既定のパラメーターなしのコンストラクターがないため失敗します。 この問題の詳細については、次の 2 つの ASP.NET フォーラムの投稿: 厳密に型指定されたデータセットを使用した ObjectDataSource の更新の記事と、ObjectDataSource と厳密に型指定されたデータセットの問題の記事を参照してください。
次に、AddProduct
と UpdateProduct
の両方で、コードによって ProductsRow
インスタンスが作成され、渡された値が設定されます。 DataRow の DataColumns に値を割り当てると、さまざまなフィールド レベルの検証チェックが行われる可能性があります。 そのため、渡された値を手動で DataRow に戻すと、BLL メソッドに渡されるデータの有効性が確保されます。 残念ながら、Visual Studio によって生成される厳密に型指定された DataRow クラスでは、null 許容型は使用されません。 代わりに、DataRow 内の特定の DataColumn が NULL
データベース値に対応する必要があることを示すには、SetColumnNameNull()
メソッドを使用する必要があります。
UpdateProduct
では、最初に GetProductByProductID(productID)
を使用して更新するために製品を読み込みます。 これは無用にデータベースにアクセスしているように思えるかもしれませんが、この追加アクセスは、オプティミスティック同時実行制御について学習する今後のチュートリアルで役に立ちます。 オプティミスティック同時実行制御は、同じデータに対して同時に作業している 2 人のユーザーが誤って互いの変更を上書きしないようにする手法です。 また、レコード全体を取得すると、DataRow の列のサブセットのみを変更する更新メソッドを BLL に簡単に作成できます。 SuppliersBLL
クラスを探索すると、このような例があります。
最後に、ProductsBLL
クラスには DataObject 属性が適用されており (ファイルの先頭付近にあるクラス ステートメントの直前の [System.ComponentModel.DataObject]
構文)、メソッドには DataObjectMethodAttribute 属性があることに注意してください。 DataObject
属性は、クラスを ObjectDataSource コントロールへのバインドに適したオブジェクトとしてマークしますが、DataObjectMethodAttribute
はメソッドの目的を示します。 今後のチュートリアルで説明するように、ASP.NET 2.0 の ObjectDataSource を使用すると、クラスからデータに宣言的にアクセスしやすくなります。 ObjectDataSource のウィザードでバインドできるクラスの一覧をフィルター処理できるように、既定では、ウィザードのドロップダウン リストに表示されるのは、DataObjects
としてマークされているクラスのみです。 ProductsBLL
クラスは、これらの属性なしでも同様に機能しますが、これらを追加すると、ObjectDataSource のウィザードで簡単に操作できます。
その他のクラスの追加
ProductsBLL
クラスが完成したら、カテゴリ、サプライヤー、従業員を操作するためのクラスを追加する必要があります。 上の例の概念を使用して、次のクラスとメソッドを作成します。
CategoriesBLL.cs
GetCategories()
GetCategoryByCategoryID(categoryID)
SuppliersBLL.cs
GetSuppliers()
GetSupplierBySupplierID(supplierID)
GetSuppliersByCountry(country)
UpdateSupplierAddress(supplierID, address, city, country)
EmployeesBLL.cs
GetEmployees()
GetEmployeeByEmployeeID(employeeID)
GetEmployeesByManager(managerID)
注目すべき 1 つのメソッドは、SuppliersBLL
クラスの UpdateSupplierAddress
メソッドです。 このメソッドは、サプライヤーの住所情報のみを更新するためのインターフェイスを提供します。 内部的には、このメソッドは、SupplierDataRow
オブジェクトで、指定された supplierID
(GetSupplierBySupplierID
を使用) を読み取り、そのアドレス関連のプロパティを設定し、SupplierDataTable
の Update
メソッドに呼び出します。 次に UpdateSupplierAddress
メソッドを示します。
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateSupplierAddress(ByVal supplierID As Integer, _
ByVal address As String, ByVal city As String, ByVal country As String) _
As Boolean
Dim suppliers As Northwind.SuppliersDataTable = _
Adapter.GetSupplierBySupplierID(supplierID)
If suppliers.Count = 0 Then
Return False
Else
Dim supplier As Northwind.SuppliersRow = suppliers(0)
If address Is Nothing Then
supplier.SetAddressNull()
Else
supplier.Address = address
End If
If city Is Nothing Then
supplier.SetCityNull()
Else
supplier.City = city
End If
If country Is Nothing Then
supplier.SetCountryNull()
Else
supplier.Country = country
End If
Dim rowsAffected As Integer = Adapter.Update(supplier)
Return rowsAffected = 1
End If
End Function
BLL クラスの完全な実装については、この記事のダウンロードを参照してください。
手順 2: 型指定されたデータセットへの BLL クラスを介したアクセス
最初のチュートリアルでは、型指定されたデータセットをプログラムで直接処理する例を見ましたが、BLL クラスを追加すると、プレゼンテーション層は代わりに BLL を処理する必要があります。 最初のチュートリアルの AllProducts.aspx
の例で、ProductsTableAdapter
は、次のコードに示すように、製品の一覧を GridView にバインドするために使用されていました。
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
新しい BLL クラスを使用するには、変更する必要があるのは、ProductsTableAdapter
オブジェクトを ProductBLL
オブジェクトに置き換えるだけの最初のコード行だけです。
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
BLL クラスには、ObjectDataSource を使用して (型指定されたデータセットと同様に) 宣言的にアクセスすることもできます。 次のチュートリアルでは、ObjectDataSource について詳しく説明します。
図 3: 製品の一覧が GridView に表示されます (クリックするとフルサイズの画像が表示されます)。
手順 3: DataRow クラスへのフィールド レベルの検証の追加
フィールド レベルの検証は、挿入または更新時のビジネス オブジェクトのプロパティ値に関連するチェックです。 製品のフィールド レベルの検証規則には、次のようなものがあります。
ProductName
フィールドの長さは 40 文字以下にする必要がありますQuantityPerUnit
フィールドの長さは 20 文字以下にする必要がありますProductID
、ProductName
、Discontinued
フィールドは必須ですが、他のすべてのフィールドは省略可能ですUnitPrice
、UnitsInStock
、UnitsOnOrder
、ReorderLevel
フィールドは 0 以上である必要があります
これらのルールは、データベース レベルで表現でき、そのようにする必要があります。 ProductName
フィールドと QuantityPerUnit
フィールドの文字制限は、Products
テーブル内の列のデータ型によってキャプチャされます (それぞれ nvarchar(40)
と nvarchar(20)
)。 フィールドが必須か省略可能かどうかは、データベース テーブル列で NULL
が許可されるかどうかで示されます。 4 つのチェック制約が存在し、これらは、0 以上の値のみが UnitPrice
、UnitsInStock
、UnitsOnOrder
、または ReorderLevel
列に設定されるように確認します。
これらの規則は、データベースに適用するだけでなく、データセット レベルでも適用する必要があります。 実際、フィールドの長さと、値が必須か省略可能かは、DataTable の DataColumns のセットごとに既にキャプチャされています。 既存のフィールド レベルの検証が自動的に提供されるのを確認するには、データセット デザイナーに移動し、いずれかの DataTable からフィールドを選択し、[プロパティ] ウィンドウに移動します。 図 4 に示すように、ProductsDataTable
の QuantityPerUnit
DataColumn の最大長は 20 文字で、NULL
値が許可されます。 ProductsDataRow
の QuantityPerUnit
プロパティを 20 文字を超える文字列値に設定しようとすると、ArgumentException
がスローされます。
図 4: DataColumn は基本的なフィールド レベルの検証を提供する (クリックするとフルサイズの画像が表示されます)
残念ながら、[プロパティ] ウィンドウでは、UnitPrice
値が 0 以上である必要があるなどの境界チェックを指定できません。 この種類のフィールド レベルの検証を提供するには、DataTable の ColumnChanging イベントのイベント ハンドラーを作成する必要があります。 前のチュートリアルで説明したように、型指定されたデータセットによって作成された DataSet、DataTables、DataRow オブジェクトは、部分クラスを使用して拡張できます。 この手法を使用して、ProductsDataTable
クラスの ColumnChanging
イベント ハンドラーを作成できます。 まず、ProductsDataTable.ColumnChanging.vb
という名前の App_Code
フォルダーにクラスを作成します。
図 5: App_Code
フォルダーに新しいクラスを追加する (クリックするとフルサイズの画像が表示されます)
次に、ColumnChanging
イベントのイベント ハンドラーを作成します。これは、UnitPrice
、UnitsInStock
、UnitsOnOrder
、ReorderLevel
列の値 (NULL
でない場合) が 0 以上であることを確認します。 このような列が範囲外の場合は、ArgumentException
をスローします。
ProductsDataTable.ColumnChanging.vb
Imports System.data
Partial Public Class Northwind
Partial Public Class ProductsDataTable
Public Overrides Sub BeginInit()
AddHandler Me.ColumnChanging, AddressOf ValidateColumn
End Sub
Sub ValidateColumn(sender As Object, e As DataColumnChangeEventArgs)
If e.Column.Equals(Me.UnitPriceColumn) Then
If Not Convert.IsDBNull(e.ProposedValue) AndAlso _
CType(e.ProposedValue, Decimal) < 0 Then
Throw New ArgumentException( _
"UnitPrice cannot be less than zero", "UnitPrice")
End If
ElseIf e.Column.Equals(Me.UnitsInStockColumn) OrElse _
e.Column.Equals(Me.UnitsOnOrderColumn) OrElse _
e.Column.Equals(Me.ReorderLevelColumn) Then
If Not Convert.IsDBNull(e.ProposedValue) AndAlso _
CType(e.ProposedValue, Short) < 0 Then
Throw New ArgumentException(String.Format( _
"{0} cannot be less than zero", e.Column.ColumnName), _
e.Column.ColumnName)
End If
End If
End Sub
End Class
End Class
手順 4: BLL のクラスへのカスタム ビジネス ルールの追加
フィールド レベルの検証に加えて、単一列レベルでは表現できないさまざまなエンティティまたは概念を含む、高レベルのカスタム ビジネス ルールが存在する場合があります。次に例を挙げます。
- 製品が廃止された場合に、その
UnitPrice
は更新できない - 従業員の居住国は、その上司の居住国と同じである必要がある
- サプライヤーが提供する唯一の製品は廃止することができない
BLL クラスには、アプリケーションのビジネス ルールに準拠していることを確認するためのチェックが含まれている必要があります。 これらのチェックは、適用するメソッドに直接追加できます。
特定のサプライヤーの唯一の製品は廃止とマークできないことが、ビジネス ルールによって規定されるとします。 つまり、製品 X がサプライヤー Y から購入した唯一の製品である場合は、X を廃止済みとしてマークできませんでした。ただし、サプライヤー Y が A、B、C の 3 つの製品を供給した場合は、これらすべてを廃止済みとしてマークできます。 奇妙なビジネス ルールですが、ビジネス ルールと常識が常に一致するとは限りません。
このビジネス ルールを UpdateProducts
メソッドに適用するには、まず、Discontinued
が True
に設定されていたかどうかを確認し、設定されていた場合は、GetProductsBySupplierID
を呼び出して、この製品のサプライヤーから購入した製品の数を確認します。 このサプライヤーから購入した製品が 1 つだけの場合は、ApplicationException
をスローします。
<System.ComponentModel.DataObjectMethodAttribute_
(System.ComponentModel.DataObjectMethodType.Update, True)> _
Public Function UpdateProduct( _
productName As String, supplierID As Nullable(Of Integer), _
categoryID As Nullable(Of Integer), quantityPerUnit As String, _
unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _
unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _
discontinued As Boolean, 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 discontinued Then
Dim productsBySupplier As Northwind.ProductsDataTable = _
Adapter.GetProductsBySupplierID(product.SupplierID)
If productsBySupplier.Count = 1 Then
Throw New ApplicationException( _
"You cannot mark a product as discontinued if it is " & _
"the only product purchased from a supplier")
End If
End If
product.ProductName = productName
If Not supplierID.HasValue Then
product.SetSupplierIDNull()
Else
product.SupplierID = supplierID.Value
End If
If Not categoryID.HasValue Then
product.SetCategoryIDNull()
Else
product.CategoryID = categoryID.Value
End If
If quantityPerUnit Is Nothing Then
product.SetQuantityPerUnitNull()
Else
product.QuantityPerUnit = quantityPerUnit
End If
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
If Not unitsOnOrder.HasValue Then
product.SetUnitsOnOrderNull()
Else
product.UnitsOnOrder = unitsOnOrder.Value
End If
If Not reorderLevel.HasValue Then
product.SetReorderLevelNull()
Else
product.ReorderLevel = reorderLevel.Value
End If
product.Discontinued = discontinued
Dim rowsAffected As Integer = Adapter.Update(product)
Return rowsAffected = 1
End Function
プレゼンテーション層での検証エラーへの応答
プレゼンテーション層から BLL を呼び出すときに、発生する可能性のある例外処理を試行するか、ASP.NET までバブリングする (これにより、HttpApplication
の Error
イベントが発生する) かを決定できます。 BLL をプログラムで操作するときに例外を処理するには、次の例に示すように、Try...Catch ブロックを使用できます。
Dim productLogic As New ProductsBLL()
Try
productLogic.UpdateProduct("Scotts Tea", 1, 1, Nothing, _
-14, 10, Nothing, Nothing, False, 1)
Catch ae As ArgumentException
Response.Write("There was a problem: " & ae.Message)
End Try
今後のチュートリアルで説明するように、データ Web コントロールを使用してデータを挿入、更新、または削除するときに BLL からバブリングする例外の処理は、コードを Try...Catch
ブロックでラップするのではなく、イベント ハンドラーで直接処理できます。
まとめ
適切に設計されたアプリケーションは、それぞれが特定のロールをカプセル化する個別のレイヤーに作成されます。 この記事シリーズの最初のチュートリアルでは、型指定されたデータセットを使用してデータ アクセス層を作成しました。このチュートリアルでは、DAL に呼び出すアプリケーションの App_Code
フォルダーに、ビジネス ロジック層を一連のクラスとして構築しました。 BLL は、アプリケーションのフィールド レベルおよびビジネス レベルのロジックを実装します。 このチュートリアルで行ったように、別の BLL を作成するだけでなく、部分クラスを使用して TableAdapters のメソッドを拡張することもできます。 ただし、この手法を使用しても、既存のメソッドをオーバーライドすることも、DAL と BLL をこの記事で取ったアプローチと同じように完全に分離することもできません。
DAL と BLL が完了したら、プレゼンテーション層に着手する準備ができました。 次のチュートリアルでは、データ アクセスのトピックから少し離れて、チュートリアル全体で使用するための一貫したページ レイアウトを定義します。
プログラミングに満足!
著者について
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、Dennis Patterson、Carlos Santos、Hilton Giesenow でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。