در
اين مبحث بر
روي قوانين
تجاري( Business rules)
دريكBusiness Logic Layer(BLL)
متمركز مي
شويم كه براي
تبادل داده ها
بين دو لايه ي Presentation و DAL
به كار مي رود.
مقدمه
لايه
ي DAL كه در
اولين بحث
ايجاد شد , كاملا از Presentation جدا
است.با وجود
اين كه DAL
جزئيات
دسترسي به
داده ها را از
لايه ي Presentation
كاملا جدا مي
كند اما گاهی
مجبور به
بكارگيري قوانین
تجاری است.براي
مثال ,
در Application که در
مبحث قبل
ساختیم , ممكن است
بخواهيم اجازه
ي ويرايش فيلد
هاي CategoryID و SupplierIDدر
جدول Product
را براي
محصولاتي كه
فيلد Discontinued آنها
مقدار 1 دارد , به
كاربر
ندهيم.يا بخواهیم
براي اعتبار
سنجي قوانيني
را وضع
كنيم.مثلا تنها
کاربران خاصی
اجازه ی حذفProduct
ها را داشته
باشند یا
اینکه افرادی
خاص بتوانند
مقدار UnitPrice را تغییر
دهند.
در
این مبحث
خواهیم دید که
چگونه قوانین
تجاری در یک Business Logic Layer(BLL) مانند یک
واسط برای
تبادل داده ها
بین لایه ی Presentation
وDAL
به کار می
رود. BLL باید
مانند یک کلاس
جداگانه برای
پروژه ایجاد
شود.در این
مبحث ما BLL را
در فلدرApp_code پیاده
سازی می کنیم.
شکل 1رابطه ی
معماری بین سه
لایه ی Presentation,BLL وِDAL
را نشان می
دهد.
شکل1:BLL لایه
ی Presentation را از
لایه ی Data Access جدا
می کند
وقوانین
تجاری را
تحمیل می کند.
مرحله
ی 1:ایجاد
کلاسهای BLL
BLL ما شامل
4 کلاس است, برای هر TableAdapter
درDAL یک کلاس
در نظر می
گیریم ;
هر کلاس BLL
متدهایی برای
بازیابی,
insert,update و delete از TableAdapter درDAL
دارد که
قوانین مقتضی
را به کار می
گیرد.
جهت
جدا کردن
کلاسهای BLL
ولایه ی DAL
ازیکدیگر, بیایید 2
زیر پوشه ی
جداگانه در
فلدرApp_Code به نامهایDAL
و BLL
ایجاد
کنیم.برای این
کار روی فلدر App_Code
در Soulution
Explorer
راست کلیک
کنید و گزینه
ی New Folder را
انتخاب کنید
ودو فلدر به
نامهای BLL وDAL
ایجاد
کنید.بعد از
ایجاد این دو
پوشه, Typed DataSet
ی را که در
مبحث قبل
ایجاد کردیم
را به زیرپوشه
ی DAL منتقل
کنید.
سپس
, 4
فایل کلاس
را در زیرپوشه
ی BLL ایجاد
کنید.جهت
انجام این کار, روی
زیرپوشه ی BLL راست
کلیک کنید, گزینه ی Add a New Item
را انتخاب
کنید و Class template را
انتخاب کنید.4
کلاس با
نامهای ProductsBLL,CategoriesBLL,SuppliersBLL و EmployeesBLL
ایجاد کنید.
شکل2:افزودن
4 کلاس به زیر
پوشه ی BLL
سپس
,
متدهایی را به
هر کلاس جهت
پنهان
سازی(بسته بندی
کردن) متدهای
تعریف شده
برای TableAdapter ها ی
ساخته شده در
مبحث قبل , اضافه می
کنیم.
توجه: اگر از VisualStudio
ورژن
استاندارد یا
بالاتر
استفاده می
کنید ,
می توانید این
کلاسها را را
به صورت Visual توسط Class Designer طراحی
کنید.
برای
کلاسProductsBLL ,
7 متد زیر را
اضافه می
کنیم:
- GetProducts() : همه
ی محصولات را
بر می گرداند.
- GetProductByProductID(productID) :همه
ی محصولات با
یک ID خاص را
برمی گرداند.
- GetProductsByCategoryID(categoryID) :همه
ی محصولات یک
مقوله ی(category)
مشخص را بر می
گرداند.
- GetProductsBySupplier(supplierID) :همه
ی محصولات تولید
شده توسط یک
تولید کننده
ی مشخص را بر
می گرداند.
- AddProduct(productName
,supplierID , categoryID , quantityPerUnit , unitPrice , unitsInStook , unitsOnOrder
, reorderLevel , discontinued) : افزودن
یک محصول
جدید به
دیتابیس با
مقدار های
پاس داده شده ; مقدار ProductID
رکورد جدید
را برمی
گرداند.
- UpdateProduct(productName
,supplierID , categoryID , quantityPerUnit , unitPrice , unitsInStook , unitsOnOrder
, reorderLevel , discontinued ,productID ) : به
روزرسانی
وویرایش یک
محصول موجود
در دیتابیس
با استفاده
از مقدارهای
پاس داده شده; اگر یک
سطر به روز
شود مقدار 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 را
فرا می خوانند, اما در
بعضی موارد
ممکن است
قوانین تجاری
داشته باشیم
که لازم است
دراین سطح
پیاده سازی شود(مانند
قواننین و
مجوزهای
اعتبارسنجی(authorization)
برای کاربر log on
شده یا اینکه
کدام کاربر مالک
است). برای این
متدها , BLL
صرفا مانند یک
پروکسی
درمیان لایه ی
Presentation ی که به
داده های لایه
ی Data Access Layer دسترسی
دارد, به کار می
رود.
متدهای
AddProduct و UpdateProduct دارای
پارامترهایی
هستند که
مقدار آنها , فیلدهای
مختلف product است
و باعث افزودن
یک محصول جدید
یا به روز رسانی
یک محصول
موجود می
شوند. از
آنجایی که بعضی
از ستونهای
جدولProduct می
توانند مقدار NULL
داشته باشند(CategoryID ,SupplierID ,UnitPrice),این
پارامترها
برای متدهای
فوق با نوع nullable
نگاشت داده می
شوند.نوعهای Nullable , نوع
جدیدی هستند
که در .NET2
اضافه شده اند
وبرای نشان
دادن نوعی
هستند که می
توانند null
باشند.درC# یک
مقدار از نوع nullable
را با افزودن
علامت"؟" بعد
از نوع
آن(برای مثال, int ? x;) ,
مشخص می کنند.
3 متد
دیگر , مقدار Boolean ی به
ازای اضافه
شدن یک سطر
جدید یا update یا delete
شدن یک سطر, برمی
گردانند.برای
مثال,
اگر یک developer متد DeleteProduct
را برای یک ProductID
ی که در جدول Product
موجود نیست,
فراخواند, عبارتDelete در
دیتابیس
تغییری را
اعمال نمی
کندوسطری را حذف
نمی کند, بنابراین
متدDeleteProduct مقدار false
را بر می
گرداند.
توجه
کنید که وقتی
یک محصول جدید
می افزایید یا
یک محصول
موجود را Update می
کنید, مقدار
فیلدهای Product جدید
یا ویرایش شده
را مانند یک
لیست اسکالر می
گیریم (می
پذیریم) در
حالی که یک
نمونهProductsRow را
می پذیرد.
کلاس ProductsRow از
کلاس DataRow درADO.NET مشتق
شده است, که به طور
پیش فرض هیچ
متد سازنده ی
بدون پارامتری
ندارد.برای
ایجاد یک
نمونه ProductsRow جدید
, ما
باید ابتدا یک
نمونه ProductsDataTable
ایجاد کنیم و
بعد متدNewProductRow آن
را
بخواهیم(چیزی
که در AddProduct
انجام دادیم).این
نقص زمانی که
برای insert یا update محصولات
از ObjectDataSource استفاده
می کنیم,به
حد خود میرسد. خلاصه, ObjectDataSource سعی می
کند یک نمونه
از پارامترهای
ورودی ایجاد
کند.اگر متدBLL
منتظر یک
نمونه ProductsRow باشد, ObjectDataSource سعی می
کند یکی ایجاد
کند, اما
بعلت فقدان یک
سازنده ی بدون
پارامتر پیش
فرض ,
متوقف می شود.
سپس
, در AddProduct
و UpdateProduct , کد یک نمونه ProductsRow
ایجاد می کندو
آن را با
مقدارهایی که
به آن پاس
داده شده است, پر می
کند. وقتی که
مقدارهایی را
به ستونهای داده(DataColumn)
در یک سطر
داده (DataRow)نسبت
می دهید,
field-level validation های
مختلفی رخ می
دهد. از اینرو, به طور
دستی passed را در
مقدارهای
برگشتی در یک DataRow
بگذارید تا از
معتبر بودن
داده های پاس
داده شده به
متد BLL مطمئن
شوید.متاسفانه, کلاسهای
Strongly-typed DataRow که توسط Visual Studio
ساخته می شوند, نوع
داده ی nullable را
استفاده نمی
کنند. برای
نشان دادن یک DataColumn
در یک DataRow مخصوص
که با مقدار Null
دیتابیس
متناظر است, باید
ازمتدSetColumnNameNull()
استفاده کنیم.
در
UpdateProduct ما ابتدا
برای لود
محصول برای update
از متد GetProductByProductID(productID) استفاده
می کنیم. به نظر می
رسد که این یک
رفت وبرگشت بی
دلیل به دیتابیس
است, اما
این سفر اضافی
باعث یک
همزمانی خوش
بینانه
ارزنده می
شود.همزمانی
خوش بینانه(Optimistic concurrency) یک تکنیک
برای اطمینان
از این است که
دو کاربر که
همزمان با یک
داده ی مشابه
کار می کنند
به طور تصادفی
روی تغییرات
یکدیگرoverwrite
ندارند.همچنین
گرفتن رکورد ورودی,ایجاد
متدهای update در BLL
که فقط
زیرمجموعه ای
از ستونهای DataRow
را ویرایش می
کند , را
آسان تر می
کند. وقتی که
کلاس SuppliersBLL را
توضیح دهیم , یک مثال
از آن را
خواهید دید.
در
آخر, توجه
کنید که در
کلاس ProductsBLL
اتربیوتDataObject
بکار
برده شده است ([System.ComponentModal.DataObject])
ومتدها
اتربیوتDataObjectMethodAttribute را
دارند. اتربیوت DataObject
کلاس را همچون
شیی مناسب
برای اتصال به
یک کنترل ObjectDataSource ,
علامت می زند , در حالی
که اتربیوتDataObjectMethodAttribute هدف متد را
نشان می دهد. با رجوع
به وب سایت ASP.NET
در مبحثهای
دیگر خواهید
دید که چگونه
یک کنترل ObjectDataSource
دسترسی
اعلانی به
داده ها از یک
کلاس را آسان می
کند. جهت کمک
به فیلتر کردن
لیست کلاسهای
ممکن برای اتصال
در ویزاردObjectDataSource ,
به طور پیش
فرض تنها آن
کلاسهایی که
با DataObject مشخص
شده اند درdrop-down
list
ویزارد نشان
داده می شود.کلاس
ProductsBLL بدون این
اتربیوت هم
کار می کند
اما با اضافه کردن
این صفت در
زمان کار با ObjectDataSourceراحتریم.
افزودن
کلاسهای دیگر
با
کامل شدن کلاس
ProductBLL
, برای
کار با مقوله
ها(categories),
تولیدکنندگان(suppliers),
کارمندان(employees) , باید
کلاسهایی را
بیفزاییم. جهت
انجام این کار, کلاسها
ومتدهای زیر
را ایجاد می
کنیم:
|
l CategoriesBLL.cs
¡ GetCategories()
¡ GetCategoryByCategoryID(categoryID)
l SuppliersBLL.cs
¡ GetSuppliers()
¡ GetSupplierBySupplierID(supplierID)
¡ GetSuppliersByCountry(country)
¡ UpdateSupplierAddress(supplierID,
address,
city,
country)
l EmployeesBLL.cs
¡ GetEmployees()
¡ GetEmployeeByEmployeeID(employeeID)
¡ GetEmployeesByManager(managerID)
|
یکی
از متدهای باارزشی
که باید به آن
توجه کرد, متد UpdateSupplierAddress کلاسSuppliersBLL
است.این متد
اینترفیسی
برای به روز
رسانی
اطلاعات آدرس
یک تولید کننده
است.این متد
از داخل شی SupplierDtaRow
برای یکsupplierID مشخص
(ازGetSupplierBySupplierID
استفاده می
کند), می
خواند,
خصوصیات آدرس
آن را تنظیم
می کند و سپس
متد Update درSupplierDataTable را
فراخوانی می
کند.متد 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 Addressrelated
information
int rowsAffected = Adapter.Update(supplier);
// Return true if
precisely one row was updated,
// otherwise false
return rowsAffected
== 1;
}
}
|
مرحله
ی 2:دسترسی به Typed DataSet در میان
کلاسهای BLL
در
مبحث قبل
مثالهایی از
چگونگی کار با
DataSet از طریق
برنامه نویسی
به طور مستقیم
را دیدیم., اما با
اضافه شدن
کلاسهای BLL ,
لایه ی Presentation باید
با لایه ی BLL کار
کند.درصفحه ی AllProducts.aspx در مبحث
قبل , ProductTableAdapter جهت
اتصال به 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 می توانند
توسط ObjectDataSource به
صورت اعلانی
نیز در دسترس
باشند. شما
می توانید با
مراجعه به وب
سایتASP.NET در
مقالات دیگر
جگونگی این
استفاده را
تحقیق کنید.
شکل 3: لیست
محصولات
نمایش داده
شده در یک GridView
مرحله
ی 3: افزودن
اعتبار سطح
فیلد(Field-Level Validation)
به کلاس های DataRow
اعتبار
سنجی سطح
فیلد(Field-Level validation)
چکهایی برای
مقدار
پراپرتیهای
شی های تجاری(business objects)در زمان insert
یا update هستند. بعضی از
این قوانین
اعتبارسنجی
سطح فیلد عبارتند
از:
- فیلدProductName باید کمتریا
مساوی 40
کارکتر باشد
- فیلدQuantityPerUnit باید کمتر
یا مساوی 20
کارکتر باشد.
- فیلدهای
ProductID,ProductName و Discontinued
ضروری هستند
وباید مقدار
داشته باشند
اما بقیه ی
فیلد ها
اختیاری اند.
- فیلدهای
UnitPrice,UnitsInStock,UnitsOnOrder و ReorderLevel باید
بزرگتر یا
مساوی صفر
باشد.
این
قوانین می
توانند و باید
درسطخ
دیتابیس تعریف
شوند.محدودیت
تعداد
کارکترهای
فیلدهایProductName وQuantityPerUnit در
زمان تعریف
نوع داده های
ستونهای جدول
Products مشخص شد(nvarchar(40)
وnvarchar(20)).همچنین
فیلدهای
ضروری و
اختیاری نیز
در زمان ایجاد
ستونهای
جدولهای
دیتابیس با چک
زدن مقدار NULLS بیان
می شود.4
محدودیت برای
اطمینان از
اینکه مقدارهای
داده شده به
فیلدهای UnitPrice,UnitsInStock,UnitsOnOrder یاReorderLevel ,بزرگتر
یا مساوی صفر
هستند .
بعلاوه
برای اجبار
این قوانین در
دیتابیس باید
در سطح DataSet نیز
اجبار
شوند.درحقیقت, طول
فیلد یا
مقداری که می
پذیرد یا
اختیاری بودن
آن برای هر
مجموعه ی DataColumn ها
از DataTable ,
کنترل شده
اند.برای
دیدن این
اعتبارسنجی
های سطح فیلد
به طور خودکار
آماده شده به DataSet Designer بروید, یک فیلد از
یک DataTable انتخاب
کنید و به
پراپرتیز window
بروید.
همانطور که
شکل 4 نشان می
دهد, ستون
داده ی QuantityPerUnit در ProductsDataTable ماکزیمم
طول 20 کارکتر
داردو می
تواند مقدار NULL
داشته
باشد.اگر سعی
کنید که
پراپرتی QuantityPerUnit در ProductsDataRowرا با
مقداربیش از 20
کارکتر , مقدار دهی
کنید , ArgumentException رخ می دهد.
شکل 4: DataColumn
اعتبارسنجی
سطح فیلد را
فراهم می کند.
متاسفانه,برای
چک کردن نمی
توانیم حدو
مرزها هایی را
مشخص کنیم, برای مثال, در میان
خصوصیات ,Window چک کردن
اینکه مقدار UnitPrice
باید بزرگتر
یا مساوی صفر
باشد.برای
فراهم کردن این
نوع از سطح
اعتبارسنجی , لازم
است که یک event
handler
برای رویدادColumnChanging
متعلق به DataTable ,
ایجاد کنیم.همانطور
که در مبحث
قبلی ذکر شد, DataSet وDataTable ها و اشیائ DataRow که
توسط Typed DataSet
ایجاد می شوند, می
توانند در
کلاسهای Partial
استفاده شوند.با
استفاده از
این تکنیک, می توانیم
یکeventr handler (کنترلگر
رویداد) ColumnChanging برای
کلاس ProductsDataTable
ایجاد می
کنیم.با ایجاد
یک کلاس با
نام
ProductsDataTable.ColumnChanging.cs در پوشه ی App_Codeکار
را شروع کنید.
شکل
5:افزودن یک
کلاس جدید در
پوشه ی App_Code
سپس
, یک event handler
برای رویدادColumnChanging
جهت اطمینان
از معتبر بودن
مقدار
ستونهای UnitPrice,UnitsInStock,UnitsOnOrder و ReorderLevel
(در صورتی که
مقدارشان NULL
نیست),
ایجاد کنیم.
اگر
هر یک از
ستونها مقدار
خارج از رنجی
داشته باشند
یک 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
علاوه
بر
اعتبارسنجی
سطح فیلد, ممکن است
قوانین تجاری
سطح بالاتری
موجود باشند
که درگیرموجودیتهای
مختلف یا
مفهوم غیر
قابل بیان در
سطح یک ستون, باشند, مانند:
- اگر
یک محصول Discontinued است, امکان
به روز کردن UnitPrice
برای آن
محصول مقدور
نباشد.
- کشور
محل اقامت
کارمندان با
کشور محل
اقامت مدیر
یکی باشد.
- اگر
محصولی تنها
محصول
تولیدی یک
تولید کننده
است , discontinuedنباشد
کلاسهای BLL
باید شامل
علامتهایی
جهت اطمینان
از تبعیت Application ازاین
قوانین تجاری
باشد.می توان
این علامتها
را مستقیما به
متدی که
میخواهد این
قوانین را به
کار گیرد ,
افزود.
تصور
کنید که قانون
تجاری ما این
باشد که اگر محصولی
تنها محصول
دریافتی از
تولید کننده
ای باشد , امکان Discontinued کردن
آن وجود
نداشته باشد.به
عبارت دیگر, اگر
محصول X تنها
محصول
خریداری شده
از تولید
کننده ی Y است, ما نمی
توانیم محصول X
را discontinued کنیم.اما
اگر
تولیدکننده ی Y
محصولات A,B وC را
تولید کند , ما
می توانیم همه
ی آن محصولات
را discontinued کنیم.
برای
اجبار این
قوانین در متد
UpdateProducts با چک کردن
این مطلب که
اگر Discontinued با
مقدار true ست
شده است, باید GetProductsBySupplierID راجهت
تعیین اینکه
ما چند محصول
را از تولید
کننده خریده
ایم, فراخوانیم.اگر
فقط یک محصول
از تولید
کننده
خریداری شده
باشد,
یک 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;
}
|
پاسخگویی
به خطاهای
اعتبارسنجی
در لایه ی Presentation
وقتی
که BLL را از
لایه ی Presentation فرا
می خوانیم, میتوانیم
تصمیماتی را
برای کنترل exception
ها که ممکن
است ظاهرشوند, اتخاذ
کنیم.جهت handle کردن
یک exception در زمان
کار با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);
}
|
exception هایی
که از سطح BLL در
هنگام
استفاده از وب
کنترل های Data در
زمانinsert,update و delete
ظاهر می شوند, را می
توان مستقیما
در یک event handler
کنترل کرد.