صفحه اول سایت arrow کامپیوتر و فناوری اطلاعات arrow آموزش زبان برنامه نویسی #C قسمت دوم : چگونگی ساخت لایه Business Logic Layer

- برای مشاهده تمام مقالات موجود در سایت و استفاده از تالار گفتمان لطفا با نام کاربری خود وارد شوید در صورتی که عضو نیستید < اینجا > کلیک کنید.


آموزش زبان برنامه نویسی #C قسمت دوم : چگونگی ساخت لایه Business Logic Layer چاپ ارسال برای دوستان
(3 :مجموع راي ها)
فرستادن :
Mohand.es
Balatarin
Donbale
نوشته شده توسط مهندس قدمیاری   

در اين مبحث بر روي قوانين تجاري( 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