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

作成者: Scott Mitchell

PDF のダウンロード

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

はじめに

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

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

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

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

手順 1: BLL クラスの作成

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

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

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

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

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

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

注意

Visual Studio Standard Edition 以降を使用している場合 (つまり、Visual Web Developer を使用していない場合)、必要に応じてクラス Designerを使用してクラスを視覚的に設計できます。 Visual Studio のこの新機能の詳細については、「クラス Designer ブログ」を参照してください。

クラスでは、 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) は、渡された値を使用してデータベース内の既存の製品を更新します。は、 true 正確に 1 行が更新された場合は を返し、 false それ以外の場合は を返します。
  • DeleteProduct(productID) 指定した製品をデータベースから削除します

ProductsBLL.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;

[System.ComponentModel.DataObject]
public class ProductsBLL
{
    private ProductsTableAdapter _productsAdapter = null;
    protected ProductsTableAdapter Adapter
    {
        get {
            if (_productsAdapter == null)
                _productsAdapter = new ProductsTableAdapter();

            return _productsAdapter;
        }
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, true)]
    public Northwind.ProductsDataTable GetProducts()
    {
        return Adapter.GetProducts();
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductByProductID(int productID)
    {
        return Adapter.GetProductByProductID(productID);
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID)
    {
        return Adapter.GetProductsByCategoryID(categoryID);
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductsBySupplierID(int supplierID)
    {
        return Adapter.GetProductsBySupplierID(supplierID);
    }
    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Insert, true)]
    public bool AddProduct(string productName, int? supplierID, int? categoryID,
        string quantityPerUnit, decimal? unitPrice,  short? unitsInStock,
        short? unitsOnOrder, short? reorderLevel, bool discontinued)
    {
        // Create a new ProductRow instance
        Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
        Northwind.ProductsRow product = products.NewProductsRow();

        product.ProductName = productName;
        if (supplierID == null) product.SetSupplierIDNull();
          else product.SupplierID = supplierID.Value;
        if (categoryID == null) product.SetCategoryIDNull();
          else product.CategoryID = categoryID.Value;
        if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
          else product.QuantityPerUnit = quantityPerUnit;
        if (unitPrice == null) product.SetUnitPriceNull();
          else product.UnitPrice = unitPrice.Value;
        if (unitsInStock == null) product.SetUnitsInStockNull();
          else product.UnitsInStock = unitsInStock.Value;
        if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
          else product.UnitsOnOrder = unitsOnOrder.Value;
        if (reorderLevel == null) product.SetReorderLevelNull();
          else product.ReorderLevel = reorderLevel.Value;
        product.Discontinued = discontinued;

        // Add the new product
        products.AddProductsRow(product);
        int rowsAffected = Adapter.Update(products);

        // Return true if precisely one row was inserted,
        // otherwise false
        return rowsAffected == 1;
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Update, true)]
    public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
        string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
        short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
    {
        Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
        if (products.Count == 0)
            // no matching record found, return false
            return false;

        Northwind.ProductsRow product = products[0];

        product.ProductName = productName;
        if (supplierID == null) product.SetSupplierIDNull();
          else product.SupplierID = supplierID.Value;
        if (categoryID == null) product.SetCategoryIDNull();
          else product.CategoryID = categoryID.Value;
        if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
          else product.QuantityPerUnit = quantityPerUnit;
        if (unitPrice == null) product.SetUnitPriceNull();
          else product.UnitPrice = unitPrice.Value;
        if (unitsInStock == null) product.SetUnitsInStockNull();
          else product.UnitsInStock = unitsInStock.Value;
        if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
          else product.UnitsOnOrder = unitsOnOrder.Value;
        if (reorderLevel == null) product.SetReorderLevelNull();
          else product.ReorderLevel = reorderLevel.Value;
        product.Discontinued = discontinued;

        // Update the product record
        int rowsAffected = Adapter.Update(product);

        // Return true if precisely one row was updated,
        // otherwise false
        return rowsAffected == 1;
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Delete, true)]
    public bool DeleteProduct(int productID)
    {
        int rowsAffected = Adapter.Delete(productID);

        // Return true if precisely one row was deleted,
        // otherwise false
        return rowsAffected == 1;
    }
}

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

メソッドと UpdateProduct メソッドはAddProductどちらも、さまざまな製品フィールドの値をパラメーターとして受け取り、新しい製品を追加するか、既存の製品を更新します。 テーブルの列のProduct多くは値 (CategoryID、、SupplierIDUnitPriceを受け取っていくつかの名前を付けることができます) を受け取NULLることができるため、このような列にマップされる および UpdateProduct の入力パラメーターAddProductnull 許容型を使用します。 Null 許容型は .NET 2.0 の新しいものであり、値型を にするかどうかを示す手法を null提供します。 C# では、 型の後に ( などint? x;) を追加することで、値型に null 許容型としてフラグを設定?できます。 詳細については、「C# プログラミング ガイド」の「Null 許容型」セクションを参照してください。

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

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

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

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

最後に、クラスに DataObject 属性が適用されており ProductsBLL ([System.ComponentModel.DataObject]ファイルの先頭付近にあるクラス ステートメントの直前の構文)、メソッドに DataObjectMethodAttribute 属性があることに注意してください。 属性は、クラスを ObjectDataSource コントロールへのバインドに適したオブジェクトとしてマークしますDataObjectMethodAttributeが、 は DataObject メソッドの目的を示します。 今後のチュートリアルで説明するように、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 メソッドです。 このメソッドは、仕入先の住所情報のみを更新するためのインターフェイスを提供します。 内部的には、このメソッドは、指定された supplierID (を使用してGetSupplierBySupplierID) オブジェクトを読み取SupplierDataRowり、そのアドレス関連のプロパティを設定してから、 の メソッドをSupplierDataTableUpdate呼び出します。 メソッドは UpdateSupplierAddress 次のとおりです。

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateSupplierAddress
    (int supplierID, string address, string city, string country)
{
    Northwind.SuppliersDataTable suppliers =
        Adapter.GetSupplierBySupplierID(supplierID);
    if (suppliers.Count == 0)
        // no matching record found, return false
        return false;
    else
    {
        Northwind.SuppliersRow supplier = suppliers[0];

        if (address == null) supplier.SetAddressNull();
          else supplier.Address = address;
        if (city == null) supplier.SetCityNull();
          else supplier.City = city;
        if (country == null) supplier.SetCountryNull();
          else supplier.Country = country;

        // Update the supplier Address-related information
        int rowsAffected = Adapter.Update(supplier);

        // Return true if precisely one row was updated,
        // otherwise false
        return rowsAffected == 1;
    }
}

BLL クラスの完全な実装については、この記事のダウンロードを参照してください。

手順 2: BLL クラスを使用して型指定されたデータセットにアクセスする

最初のチュートリアルでは、プログラムで Typed DataSet を直接操作する例を見ましたが、BLL クラスを追加すると、プレゼンテーション層は代わりに BLL に対して機能する必要があります。 最初のチュートリアルの AllProducts.aspx 例では、 ProductsTableAdapter を使用して、次のコードに示すように、製品の一覧を GridView にバインドしました。

ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
GridView1.DataSource = productsAdapter.GetProducts();
GridView1.DataBind();

新しい BLL クラスを使用するには、変更する必要があるコードの最初の行だけをオブジェクトProductBLLProductsTableAdapter置き換えます。

ProductsBLL productLogic = new ProductsBLL();
GridView1.DataSource = productLogic.GetProducts();
GridView1.DataBind();

BLL クラスには、ObjectDataSource を使用して宣言型 (Typed DataSet と同様) にアクセスすることもできます。 次のチュートリアルでは、ObjectDataSource について詳しく説明します。

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

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

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

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

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

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

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

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

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

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

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

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

次に、 イベントのイベント ハンドラーをColumnChanging作成して、、および ReorderLevel 列の値 (そうでないNULL場合) が 0 以上であることを確認UnitPriceUnitsInStockUnitsOnOrderします。 このような列が範囲外の場合は、 をスローします ArgumentException

ProductsDataTable.ColumnChanging.cs

public partial class Northwind
{
    public partial class ProductsDataTable
    {
        public override void BeginInit()
         {
            this.ColumnChanging += ValidateColumn;
         }

         void ValidateColumn(object sender,
           DataColumnChangeEventArgs e)
         {
            if(e.Column.Equals(this.UnitPriceColumn))
            {
               if(!Convert.IsDBNull(e.ProposedValue) &&
                  (decimal)e.ProposedValue < 0)
               {
                  throw new ArgumentException(
                      "UnitPrice cannot be less than zero", "UnitPrice");
               }
            }
            else if (e.Column.Equals(this.UnitsInStockColumn) ||
                     e.Column.Equals(this.UnitsOnOrderColumn) ||
                     e.Column.Equals(this.ReorderLevelColumn))
            {
                if (!Convert.IsDBNull(e.ProposedValue) &&
                    (short)e.ProposedValue < 0)
                {
                    throw new ArgumentException(string.Format(
                        "{0} cannot be less than zero", e.Column.ColumnName),
                        e.Column.ColumnName);
                }
            }
         }
    }
}

手順 4: BLL のクラスにカスタム ビジネス ルールを追加する

フィールド レベルの検証に加えて、次のような単一列レベルでは表現できないさまざまなエンティティや概念を含む高レベルのカスタム ビジネス ルールが存在する場合があります。

  • 製品が廃止された場合、その UnitPrice 製品は更新できません
  • 従業員の居住国は、上司の居住国と同じである必要があります
  • サプライヤーが提供する唯一の製品である場合、製品を中止することはできません

BLL クラスには、アプリケーションのビジネス ルールへの準拠を確認するためのチェックが含まれている必要があります。 これらのチェックは、適用先のメソッドに直接追加できます。

特定のサプライヤーからの唯一の製品である場合、製品を廃止とマークできなかったことがビジネス ルールで規定されていることを想像してください。 つまり、製品 X がサプライヤー Y から購入した唯一の製品である場合、 X を廃止済みとしてマークすることはできません。ただし、サプライヤー Y から ABC の 3 つの製品が提供された場合は、これらすべてを廃止としてマークできます。 奇妙なビジネス ルールですが、ビジネス ルールと常識が常に一致するとは限りません。

メソッドでこのビジネス ルールを UpdateProducts 適用するには、 が Discontinuedtrue 設定されているかどうかを確認することから始めます。設定されている場合は、 を呼び出 GetProductsBySupplierID して、この製品のサプライヤーから購入した製品の数を決定します。 このサプライヤーから購入した製品が 1 つだけの場合は、 をスローします ApplicationException

public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
    string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
    short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
{
    Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
    if (products.Count == 0)
        // no matching record found, return false
        return false;

    Northwind.ProductsRow product = products[0];

    // Business rule check - cannot discontinue
    // a product that is supplied by only
    // one supplier
    if (discontinued)
    {
        // Get the products we buy from this supplier
        Northwind.ProductsDataTable productsBySupplier =
            Adapter.GetProductsBySupplierID(product.SupplierID);

        if (productsBySupplier.Count == 1)
            // this is the only product we buy from this supplier
            throw new ApplicationException(
                "You cannot mark a product as discontinued if it is the only
                  product purchased from a supplier");
    }

    product.ProductName = productName;
    if (supplierID == null) product.SetSupplierIDNull();
      else product.SupplierID = supplierID.Value;
    if (categoryID == null) product.SetCategoryIDNull();
      else product.CategoryID = categoryID.Value;
    if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
      else product.QuantityPerUnit = quantityPerUnit;
    if (unitPrice == null) product.SetUnitPriceNull();
      else product.UnitPrice = unitPrice.Value;
    if (unitsInStock == null) product.SetUnitsInStockNull();
      else product.UnitsInStock = unitsInStock.Value;
    if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
      else product.UnitsOnOrder = unitsOnOrder.Value;
    if (reorderLevel == null) product.SetReorderLevelNull();
      else product.ReorderLevel = reorderLevel.Value;
    product.Discontinued = discontinued;

    // Update the product record
    int rowsAffected = Adapter.Update(product);

    // Return true if precisely one row was updated,
    // otherwise false
    return rowsAffected == 1;
}

プレゼンテーション層での検証エラーへの対応

プレゼンテーション層から BLL を呼び出すときに、発生する可能性のある例外を処理するか、(イベントを発生HttpApplicationErrorさせる) ASP.NET までバブルアップさせるかを決定できます。 プログラムで BLL を操作するときに例外を処理するには、 try... を使用します。次 の例に示すように、catch ブロック。

ProductsBLL productLogic = new ProductsBLL();

// Update information for ProductID 1
try
{
    // This will fail since we are attempting to use a
    // UnitPrice value less than 0.
    productLogic.UpdateProduct(
        "Scott s Tea", 1, 1, null, -14m, 10, null, null, false, 1);
}
catch (ArgumentException ae)
{
    Response.Write("There was a problem: " + ae.Message);
}

今後のチュートリアルで説明するように、データ 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行をドロップしてください。