このチュートリアルでは、プレゼンテーション 層と DAL 間のデータ交換の仲介役となるビジネス ロジック レイヤー (BLL) にビジネス ルールを一元化する方法について説明します。
イントロダクション
最初のチュートリアルで作成されたデータ アクセス層 (DAL) は、データ アクセス ロジックとプレゼンテーション ロジックを完全に分離します。 ただし、DAL はデータ アクセスの詳細をプレゼンテーション層からきれいに分離しますが、適用される可能性のあるビジネス ルールは適用されません。 たとえば、アプリケーションでは、CategoryID
フィールドが 1 に設定されている場合に、SupplierID
テーブルのProducts
フィールドまたはDiscontinued
フィールドを変更できないようにしたり、年長規則を適用したり、従業員が従業員の後に雇用された人によって管理される状況を禁止したりできます。 もう 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
フォルダーに 2 つのサブフォルダー ( DAL
と BLL
) を作成しましょう。 ソリューション エクスプローラーで App_Code
フォルダーを右クリックし、[新しいフォルダー] を選択するだけです。 これら 2 つのフォルダーを作成した後、最初のチュートリアルで作成した Typed DataSet を DAL
サブフォルダーに移動します。
次に、 BLL
サブフォルダーに 4 つの BLL クラス ファイルを作成します。 これを行うには、 BLL
サブフォルダーを右クリックし、[新しい項目の追加] を選択して、クラス テンプレートを選択します。
ProductsBLL
、CategoriesBLL
、SuppliersBLL
、EmployeesBLL
の 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.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;
}
}
データ GetProducts
、 GetProductByProductID
、 GetProductsByCategoryID
、 GetProductBySuppliersID
を返すメソッドは、DAL に呼び出すだけなので、非常に簡単です。 一部のシナリオでは、このレベルで実装する必要があるビジネス ルール (現在ログオンしているユーザーやユーザーが属するロールに基づく承認規則など) がある場合がありますが、これらのメソッドは as-isのままにします。 これらのメソッドの場合、BLL は、プレゼンテーション 層がデータ アクセス層から基になるデータにアクセスするプロキシとして機能します。
AddProduct
とUpdateProduct
の両方のメソッドは、さまざまな製品フィールドの値をパラメーターとして受け取り、新しい製品を追加するか、既存のものを更新します。
Product
テーブルの列の多くは、NULL
値 (CategoryID
、SupplierID
、UnitPrice
など) を受け取ることができるため、このような列にマップされるAddProduct
とUpdateProduct
の入力パラメーターには null 許容型が使用されます。 Null 許容型は .NET 2.0 の新機能であり、代わりに値型を null
する必要があるかどうかを示す手法を提供します。 C# では、型の後に ?
( int? x;
など) を追加することで、値型に null 許容型のフラグを設定できます。 詳細については、「Null 許容型」セクションをC# プログラミング ガイドで参照してください。
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 の問題。
次に、 AddProduct
と UpdateProduct
の両方で、コードによって 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
メソッドです。 このメソッドは、サプライヤーの住所情報のみを更新するためのインターフェイスを提供します。 内部的には、このメソッドは、指定したSupplierDataRow
のsupplierID
オブジェクトを読み取り (GetSupplierBySupplierID
を使用)、そのアドレス関連のプロパティを設定し、SupplierDataTable
のUpdate
メソッドを呼び出します。
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 クラスを使用するには、変更する必要があるのが、 ProductsTableAdapter
オブジェクトを ProductBLL
オブジェクトに置き換えるだけのコードの最初の行だけです。
ProductsBLL productLogic = new ProductsBLL();
GridView1.DataSource = productLogic.GetProducts();
GridView1.DataBind();
BLL クラスは、型指定された DataSet と同様に、ObjectDataSource を使用して宣言的にアクセスすることができます。 次のチュートリアルでは、ObjectDataSource について詳しく説明します。
図 3: 製品の一覧が GridView に表示されます (フルサイズの画像を表示する をクリックします)。
手順 3: DataRow クラスに Field-Level 検証を追加する
フィールド レベルの検証は、挿入または更新時のビジネス オブジェクトのプロパティ値に関連するチェックです。 製品のフィールド レベルの検証規則には、次のようなものがあります。
-
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
列に変換できるようにします。
データベースでこれらの規則を適用するだけでなく、DataSet レベルでも適用する必要があります。 実際、フィールドの長さと値が必須か省略可能かは、DataTable の DataColumns のセットごとに既にキャプチャされています。 既存のフィールド レベルの検証が自動的に提供されるのを確認するには、DataSet デザイナーに移動し、いずれかの DataTable からフィールドを選択し、[プロパティ] ウィンドウに移動します。 図 4 に示すように、QuantityPerUnit
のProductsDataTable
DataColumn の最大長は 20 文字で、NULL
値を使用できます。
ProductsDataRow
のQuantityPerUnit
プロパティを20文字を超える文字列に設定しようとした場合、ArgumentException
がスローされます。
図 4: DataColumn は基本的な Field-Level 検証を提供します (フルサイズの画像を表示する をクリックします)。
残念ながら、プロパティ ウィンドウを使用して、 UnitPrice
値を 0 以上にする必要があるなどの境界チェックを指定することはできません。 この種類のフィールド レベルの検証を提供するには、DataTable の ColumnChanging イベントのイベント ハンドラーを作成する必要があります。
前のチュートリアルで説明したように、型指定された DataSet によって作成された DataSet、DataTables、および DataRow オブジェクトは、部分クラスを使用して拡張できます。 この手法を使用して、ColumnChanging
クラスのProductsDataTable
イベント ハンドラーを作成できます。 まず、App_Code
という名前のProductsDataTable.ColumnChanging.cs
フォルダーにクラスを作成します。
図 5: App_Code
フォルダーに新しいクラスを追加します (フルサイズの画像を表示する をクリックします)。
次に、 ColumnChanging
イベントのイベント ハンドラーを作成して、 UnitPrice
、 UnitsInStock
、 UnitsOnOrder
、および ReorderLevel
列の値 ( NULL
でない場合) が 0 以上であることを確認します。 このような列が範囲外の場合は、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 が A、 B、 Cの3つの製品を供給した場合、これらすべてを廃止としてマークすることができます。 奇妙なビジネス ルールですが、ビジネス ルールと常識が常に一致するとは限りません。
UpdateProducts
メソッドでこのビジネス ルールを適用するには、まず、Discontinued
がtrue
に設定されているかどうかを確認し、設定されている場合は、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 を呼び出すときに、発生する可能性のある例外を処理するか、( HttpApplication
の Error
イベントを発生させる) 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 は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズ・ティーチ・セルフ ASP.NET 24時間で2.0です。 彼には mitchell@4GuysFromRolla.comで連絡できます。
特別な感謝
このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Liz Shulok、Dennis Patterson、Carlos Santos、Hilton Giesenow でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.comにメッセージを送ってください。