次の方法で共有


ビジネス ロジック層を作成する (VB)

スコット・ミッチェル著

PDF をダウンロードする

このチュートリアルでは、プレゼンテーション 層と DAL 間のデータ交換の仲介役となるビジネス ロジック レイヤー (BLL) にビジネス ルールを一元化する方法について説明します。

イントロダクション

最初のチュートリアルで作成されたデータ アクセス層 (DAL) は、データ アクセス ロジックとプレゼンテーション ロジックを完全に分離します。 ただし、DAL はデータ アクセスの詳細をプレゼンテーション層からきれいに分離しますが、適用される可能性のあるビジネス ルールは適用されません。 たとえば、アプリケーションでは、CategoryID フィールドが 1 に設定されている場合に、SupplierID テーブルのProductsフィールドまたはDiscontinued フィールドを変更できないようにしたり、年長規則を適用したり、従業員が従業員の後に雇用された人によって管理される状況を禁止したりできます。 もう 1 つの一般的なシナリオは、特定のロールのユーザーのみが製品を削除したり、 UnitPrice 値を変更したりできる承認です。

このチュートリアルでは、プレゼンテーション 層と DAL 間のデータ交換の仲介役となるビジネス ロジック 層 (BLL) にこれらのビジネス ルールを一元化する方法について説明します。 実際のアプリケーションでは、BLL を別のクラス ライブラリ プロジェクトとして実装する必要があります。ただし、これらのチュートリアルでは、プロジェクト構造を簡略化するために、 App_Code フォルダーに一連のクラスとして BLL を実装します。 図 1 は、プレゼンテーション 層、BLL、DAL 間のアーキテクチャの関係を示しています。

BLL は、プレゼンテーション層をデータ アクセス層から分離し、ビジネス ルールを強制します

図 1: BLL はプレゼンテーション層をデータ アクセス層から分離し、ビジネス ルールを課す

ビジネス ロジックを実装するために個別のクラスを作成するのではなく、このロジックを部分クラスを持つ型指定された DataSet に直接配置することもできます。 型指定された DataSet の作成と拡張の例については、最初のチュートリアルを参照してください。

手順 1: BLL クラスの作成

BLL は、DAL の TableAdapter ごとに 1 つずつ、4 つのクラスで構成されます。これらの各 BLL クラスには、DAL 内のそれぞれの TableAdapter から取得、挿入、更新、および削除を行い、適切なビジネス ルールを適用するためのメソッドが用意されています。

DAL 関連のクラスと BLL 関連のクラスをより正確に分離するには、 App_Code フォルダーに 2 つのサブフォルダー ( DALBLL) を作成しましょう。 ソリューション エクスプローラーで App_Code フォルダーを右クリックし、[新しいフォルダー] を選択するだけです。 これら 2 つのフォルダーを作成した後、最初のチュートリアルで作成した Typed DataSet を DAL サブフォルダーに移動します。

次に、 BLL サブフォルダーに 4 つの BLL クラス ファイルを作成します。 これを行うには、 BLL サブフォルダーを右クリックし、[新しい項目の追加] を選択して、クラス テンプレートを選択します。 ProductsBLLCategoriesBLLSuppliersBLLEmployeesBLLの 4 つのクラスに名前を付けます。

App_Code フォルダーに 4 つの新しいクラスを追加する

図 2: App_Code フォルダーに 4 つの新しいクラスを追加する

次に、各クラスにメソッドを追加して、最初のチュートリアルの TableAdapters に対して定義されたメソッドをラップします。 現時点では、これらのメソッドは DAL に直接呼び出すだけです。後で戻り、必要なビジネス ロジックを追加します。

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

データ GetProductsGetProductByProductIDGetProductsByCategoryIDGetProductBySuppliersID を返すメソッドは、DAL に呼び出すだけなので、非常に簡単です。 一部のシナリオでは、このレベルで実装する必要があるビジネス ルール (現在ログオンしているユーザーやユーザーが属するロールに基づく承認規則など) がある場合がありますが、これらのメソッドは as-isのままにします。 これらのメソッドの場合、BLL は、プレゼンテーション 層がデータ アクセス層から基になるデータにアクセスするプロキシとして機能します。

AddProductUpdateProductの両方のメソッドは、さまざまな製品フィールドの値をパラメーターとして受け取り、新しい製品を追加するか、既存のものを更新します。 Productテーブルの列の多くは、NULL値 (CategoryIDSupplierIDUnitPriceなど) を受け取ることができるため、このような列にマップされるAddProductUpdateProductの入力パラメーターには null 許容型が使用されます。 Null 許容型は .NET 2.0 の新機能であり、代わりに値型を Nothingする必要があるかどうかを示す手法を提供します。 詳しくは、 Paul Vick のブログ エントリ「 Null 許容型と VB に関する真実 」と 、Null 許容 構造体の技術ドキュメントを参照してください。

3 つのメソッドはすべて、操作が影響を受ける行にならない可能性があるため、行が挿入、更新、または削除されたかどうかを示すブール値を返します。 たとえば、ページ開発者が存在しない製品のDeleteProductを渡してProductIDを呼び出した場合、データベースに対して発行されたDELETEステートメントは影響を与えないため、DeleteProductメソッドはFalseを返します。

新しい製品を追加したり、既存の製品を更新したりする場合、 ProductsRow インスタンスを受け入れるのではなく、新規または変更された製品のフィールド値をスカラーのリストとして取り込むことに注意してください。 この方法は、 ProductsRow クラスが既定のパラメーターなしのコンストラクターを持たない ADO.NET DataRow クラスから派生しているために選択されました。 新しい ProductsRow インスタンスを作成するには、まず ProductsDataTable インスタンスを作成してから、その NewProductRow() メソッド ( AddProductで実行) を呼び出す必要があります。 この欠点は、ObjectDataSource を使用して製品の挿入および更新を行う際に明らかになります。 つまり、ObjectDataSource は入力パラメーターのインスタンスの作成を試みます。 BLL メソッドで ProductsRow インスタンスが必要な場合、ObjectDataSource はインスタンスの作成を試みますが、既定のパラメーターなしのコンストラクターがないため失敗します。 この問題の詳細については、次の 2 つの ASP.NET フォーラムの投稿を参照してください。 Strongly-Typed DataSet を使用した ObjectDataSource の更新および ObjectDataSource と Strongly-Typed DataSet の問題

次に、 AddProductUpdateProductの両方で、コードによって ProductsRow インスタンスが作成され、渡された値が設定されます。 DataRow の DataColumns に値を割り当てると、さまざまなフィールド レベルの検証チェックが行われる可能性があります。 そのため、渡された値を手動で DataRow に戻すと、BLL メソッドに渡されるデータの有効性が確保されます。 残念ながら、Visual Studio によって生成される厳密に型指定された DataRow クラスでは、null 許容型は使用されません。 むしろ、DataRow 内の特定の DataColumn が NULL データベース値に対応する必要があることを示すには、 SetColumnNameNull() メソッドを使用する必要があります。

UpdateProductでは、最初に製品を読み込み、GetProductByProductID(productID)を使用して更新します。 これはデータベースへの不要な旅行のように思えるかもしれませんが、この追加の旅行は、オプティミスティックコンカレンシーを探索する将来のチュートリアルで価値があることを証明します。 オプティミスティック コンカレンシーは、同じデータに対して同時に作業している 2 人のユーザーが誤って互いの変更を上書きしないようにする手法です。 また、レコード全体を取得すると、DataRow の列のサブセットのみを変更する更新メソッドを BLL に簡単に作成できます。 SuppliersBLL クラスを調べるときに、このような例が表示されます。

最後に、 ProductsBLL クラスには DataObject 属性 が適用されており (ファイルの先頭付近にある class ステートメントの直前の [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 メソッドです。 このメソッドは、サプライヤーの住所情報のみを更新するためのインターフェイスを提供します。 内部的には、このメソッドは、指定したSupplierDataRowsupplierID オブジェクトを読み取り (GetSupplierBySupplierIDを使用)、そのアドレス関連のプロパティを設定し、SupplierDataTableUpdate メソッドを呼び出します。 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 クラスを介して型指定されたデータセットにアクセスする

最初のチュートリアルでは、プログラムで Typed DataSet を直接操作する例を見ましたが、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 クラスは、型指定された DataSet と同様に、ObjectDataSource を使用して宣言的にアクセスすることができます。 次のチュートリアルでは、ObjectDataSource について詳しく説明します。

製品の一覧が GridView に表示される

図 3: 製品の一覧が GridView に表示されます (フルサイズの画像を表示する をクリックします)。

手順 3: DataRow クラスに Field-Level 検証を追加する

フィールド レベルの検証は、挿入または更新時のビジネス オブジェクトのプロパティ値に関連するチェックです。 製品のフィールド レベルの検証規則には、次のようなものがあります。

  • ProductName フィールドの長さは 40 文字以下にする必要があります
  • QuantityPerUnit フィールドの長さは 20 文字以下にする必要があります
  • ProductIDProductName、およびDiscontinuedフィールドは必須ですが、他のすべてのフィールドは省略可能です
  • UnitPriceUnitsInStockUnitsOnOrder、およびReorderLevelフィールドは 0 以上である必要があります

これらのルールは、データベース レベルで表現できます。また、このルールを使用する必要があります。 ProductNameフィールドとQuantityPerUnit フィールドの文字制限は、Products テーブル内の列のデータ型 (それぞれnvarchar(40)nvarchar(20)) によってキャプチャされます。 データベース テーブル列で NULL が許可されている場合、フィールドが必須で省略可能かどうかを表します。 4 つの チェック制約 が存在し、0 以上の値のみが、 UnitPriceUnitsInStockUnitsOnOrder、または ReorderLevel 列に変換できるようにします。

データベースでこれらの規則を適用するだけでなく、DataSet レベルでも適用する必要があります。 実際、フィールドの長さと値が必須か省略可能かは、DataTable の DataColumns のセットごとに既にキャプチャされています。 既存のフィールド レベルの検証が自動的に提供されるのを確認するには、DataSet デザイナーに移動し、いずれかの DataTable からフィールドを選択し、[プロパティ] ウィンドウに移動します。 図 4 に示すように、QuantityPerUnitProductsDataTable DataColumn の最大長は 20 文字で、NULL値を使用できます。 ProductsDataRowQuantityPerUnitプロパティを20文字を超える文字列に設定しようとした場合、ArgumentExceptionがスローされます。

DataColumn は基本的な Field-Level 検証を提供します

図 4: DataColumn は基本的な Field-Level 検証を提供します (フルサイズの画像を表示する をクリックします)。

残念ながら、プロパティ ウィンドウを使用して、 UnitPrice 値を 0 以上にする必要があるなどの境界チェックを指定することはできません。 この種類のフィールド レベルの検証を提供するには、DataTable の ColumnChanging イベントのイベント ハンドラーを作成する必要があります。 前のチュートリアルで説明したように、型指定された DataSet によって作成された DataSet、DataTables、および DataRow オブジェクトは、部分クラスを使用して拡張できます。 この手法を使用して、ColumnChanging クラスのProductsDataTable イベント ハンドラーを作成できます。 まず、App_Codeという名前のProductsDataTable.ColumnChanging.vb フォルダーにクラスを作成します。

App_Code フォルダーに新しいクラスを追加する

図 5: App_Code フォルダーに新しいクラスを追加します (フルサイズの画像を表示する をクリックします)。

次に、 ColumnChanging イベントのイベント ハンドラーを作成して、 UnitPriceUnitsInStockUnitsOnOrder、および 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 を廃止済みとしてマークできませんでした。しかし、 サプライヤーYABCの3つの製品を供給した場合、これらすべてを廃止としてマークすることができます。 奇妙なビジネス ルールですが、ビジネス ルールと常識が常に一致するとは限りません。

UpdateProductsメソッドでこのビジネス ルールを適用するには、まず、DiscontinuedTrueに設定されているかどうかを確認し、設定されている場合は、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 を呼び出すときに、発生する可能性のある例外を処理するか、( HttpApplicationError イベントを発生させる) ASP.NET までバブルアップさせるかを決定できます。 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 は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・セルフ ASP.NET 24時間で2.0です。 彼には mitchell@4GuysFromRolla.comで連絡できます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Liz Shulok、Dennis Patterson、Carlos Santos、Hilton Giesenow でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.comにメッセージを送ってください。