reza344
شنبه 07 اسفند 1389, 18:20 عصر
Multi tier architecture for Linq to Sql هدف این مقاله نمایش چگونگی پیاده سازی معماری چند لایه با استفاده از ابزار لینک و همچنین استفاده از ObjectDataSource بعنوان منبع داده ها در برنامه میباشد . ویژگیهای کلیدی این روش توجه به خواسته های موجود از datacontext و کلاس های موجود مخزن است که ارائه دهنده راه کارهای ساده پیاده سازی عملیات اضافه کردن ، ویرایش کردن و حذف کردن داده ها (CRUD) با استفاده از Generic DataType ها میباشد . اگر نیاز به اطلاعات بیشتر در مورد نوع داده ای Generic هستید از این مقاله (http://arjmandi.com/file.axd?file=2009%2f11%2fgeneric.pdf) استفاده کنید . در اصل در این مقاله ما قصد ایجاد لایه Data Access یا همان DAL را با استفاده از تکنولوژی Linq داریم .
Generic Entity Repository هر موجودیت بانک اطلاعاتی دارای یک کلاس مرتبط مخزن داده ای است که وظیفه انجام عملیات های مختلف CRUD را بر عهده دارد .این کلاسهای مخزن داده های از کلاس GenericRepository ارث بری میکنند که قبلاً در آن عملیاتهای مخلتف Select , Insert , Updateو Delete پیاده سازی شده است . در سورس زیر کلاس GenericRepository نمایش داده شده است .
[System.ComponentModel.DataObject]
public class GenericRepository<TEntity, TDataContext>
where TDataContext : DataContext
{
public static List<TEntity> SelectAll()
{
...
}
public static void Insert(TEntity entity)
{
...
}
public static void Update(TEntity entity)
{
...
}
public static void Delete(TEntity entity)
{
...
}
}
حالا نوبت به تعریف کلاسهای مخزن داده ای میرسد . این کلاسها که از کلاث فوق ارث بری میکنند نوع کلاس خود که آنها را با بانک اطلاعاتی متصل میکند بهمراه نام DataContex را بعنوان پارامتر ورودی کلاس وارد میکنند . در شکل زیر مراحل کار مشخص شده است .
//ProductRepository class
public class ProductRepository : GenericRepository<
Product, NorthwindDataContext>
{
}
در کد فوق کلاس Product و نام DataContext بعوان پارامترهای ورودی ارسال شده اند . توجه کنید این دو ورودی بصورت اتوماتیک توسط خود Visual Studio بصورت کاملاً ویزاردی تولید میشوند و شما کار اضافی انجام نمیدهید .
http://arjmandi.com/image.axd?picture=2009%2f11%2flinq.png
http://arjmandi.com/image.axd?picture=2009%2f11%2fsrc.png
اما کلاس ما یعنی کلاس ProductStaticRepository با استفاده از یک الگو یا T4 Template ایجاد میشود . این کلاس حاوی عملیاتهای استاندارد CRUD است .
حال در زمان صدا زدن این کلاس در لایه های بالا تر کافیست خطوط زیر را وارد نماییم .
var productRepository = new ProductRepository();
products = productRepository.SelectAll();
اما کدهای مربوط به عملیات SelectAll , Insert , ... قبلاً در یک لایه واسط تعریف شده اند . این لایه واسط GenericDataLayer میباشد . اگر قبلاً با Ado.net کار کرده باشید حتماً واسط SqlHelper رو بخاطر دارید که روالهای مانند ExecuteDataTable رو در اختیار برنامه نویس قرار میدادند . برای دسترسی این واسط بایستی از این آدرس (http://www.codeplex.com/MultiTierLinqToSql) استفاده کنید که آدرس مرجع اصلی این مقاله هم میباشد . در این تصویر لایه مورد نظر نمایش داده شده است .
استفاده از Generic به این معناست که من نیاز به نوشتن هیچ کدی در کلاس ProductRepository خودم برای انجام عملیات استاندارد CRUD ندارم و این ایمن ترین روش میباشد .
کلاس ProductRepository من امکان بهینه شدن را نیز دارد . این بدان معناست که من میتوانم در این کلاس روالهای اضافی مورد نیازم رو وارد کنم . مثلاً در مثال زیر ما یک Product رو براساس ID جستجو میکنیم . این کار با استفاده از کلمه کلیدی Partial امکان پذیر هست که به ما این امکان رو میده که به سادگی یک کلاس را به ماژولهای مختلف بشکنیم و در زمان کامپایل یک کلاس واحد داشته باشیم .
http://arjmandi.com/image.axd?picture=2009%2f11%2fgeneric_data_layer.p ng
namespace DataLayer{
public partial class ProductStaticRepository
{
public static IList GetByCategory(int id)
{
IQueryable res = SelectAll().Where(p => p.CategoryID == id);
return res.ToList();
}
public static void InsertBlank()
{
Product p = new Product { CategoryID = 1,
ProductName = "test", SupplierID = 1 };
Insert(p);
}
}
}
یکی از امکانات لینک امکان کنترل خطاها در این سطح میباشد . این خطاها بصورت رخدادهایی هستند که لینک به شما امکان پیاده سازی آنها را میدهد . البته من شخصاً سعی میکنم کاربر اطلاعات غلط وارد فرم اطلاعات نکند اما این امکان به نوعی مهم کاری محسوب میشود . کد زیر نمونه پیاده سازی رخداد OnProductNameChanging میباشد . بازهم گزینه partial امکان اتصال کلاسها رو بهم فراهم میکنه . بدین ترتیب میشه سایر رخدادها رو پیاده سازی کرد .
using System;
namespace DataLayer
{
public partial class Product
{
partial void OnProductNameChanging(string value)
{
if (value == "")
throw new Exception("Product name cannot be empty");
}
}
}
T4 Template همانطور که قبلاً گفتم برای ایجاد کلاسهای مورد نیاز از یک الگو استفاده میکنیم . برای این کار نیاز به دو فایل DataClasses.tt و L2ST4.ttinclude داریم . روش کار بصورت زیر میباشد :
ایجاد یک پروژه از نوع Class Library برای لایه داده ها
http://arjmandi.com/image.axd?picture=2009%2f11%2fclass-library.png
ایجاد یک مخزن داده لینک (DataContext) به پروژه و تغییر نام آن به DataClasses.dbml . (باید نام این فایل با نام فایل الگو یکسان باشد ) . بعد از آن اضافه کردن جداول بانک اطلاعاتی مورد نظر به لینک .
http://arjmandi.com/image.axd?picture=2009%2f11%2fdatacontext.png
اضافه کردن دو فایل الگو به پروژه . در این لحظه باید پیام اخطار زیر بروی صفحه ظاهر شود که مربوط به تولید خودکار کد توسط فایل الگو میباشد . با زدن دکمه OK فایل مورد نظر تولید میشود .
http://arjmandi.com/image.axd?picture=2009%2f11%2falert.png
در این مرحله باید کتابخانه GenericDataLayer به پروژه اضافه شود و فایل DataClasses.generated.cs که توسط MSLinqToSQLGenerator تولید شده و در کنار فایل DataClasses.dbml.layout در زیر DataClasses.dbml حذف شود تا فایلی که توسط T4 Template ایجاد شده جانشین آن شود . اکنون میتوانید پروژه را کامپایل نمایید .
http://arjmandi.com/image.axd?picture=2009%2f11%2fsolution.png
ادامه مطالب در پست بعدی
Generic Entity Repository هر موجودیت بانک اطلاعاتی دارای یک کلاس مرتبط مخزن داده ای است که وظیفه انجام عملیات های مختلف CRUD را بر عهده دارد .این کلاسهای مخزن داده های از کلاس GenericRepository ارث بری میکنند که قبلاً در آن عملیاتهای مخلتف Select , Insert , Updateو Delete پیاده سازی شده است . در سورس زیر کلاس GenericRepository نمایش داده شده است .
[System.ComponentModel.DataObject]
public class GenericRepository<TEntity, TDataContext>
where TDataContext : DataContext
{
public static List<TEntity> SelectAll()
{
...
}
public static void Insert(TEntity entity)
{
...
}
public static void Update(TEntity entity)
{
...
}
public static void Delete(TEntity entity)
{
...
}
}
حالا نوبت به تعریف کلاسهای مخزن داده ای میرسد . این کلاسها که از کلاث فوق ارث بری میکنند نوع کلاس خود که آنها را با بانک اطلاعاتی متصل میکند بهمراه نام DataContex را بعنوان پارامتر ورودی کلاس وارد میکنند . در شکل زیر مراحل کار مشخص شده است .
//ProductRepository class
public class ProductRepository : GenericRepository<
Product, NorthwindDataContext>
{
}
در کد فوق کلاس Product و نام DataContext بعوان پارامترهای ورودی ارسال شده اند . توجه کنید این دو ورودی بصورت اتوماتیک توسط خود Visual Studio بصورت کاملاً ویزاردی تولید میشوند و شما کار اضافی انجام نمیدهید .
http://arjmandi.com/image.axd?picture=2009%2f11%2flinq.png
http://arjmandi.com/image.axd?picture=2009%2f11%2fsrc.png
اما کلاس ما یعنی کلاس ProductStaticRepository با استفاده از یک الگو یا T4 Template ایجاد میشود . این کلاس حاوی عملیاتهای استاندارد CRUD است .
حال در زمان صدا زدن این کلاس در لایه های بالا تر کافیست خطوط زیر را وارد نماییم .
var productRepository = new ProductRepository();
products = productRepository.SelectAll();
اما کدهای مربوط به عملیات SelectAll , Insert , ... قبلاً در یک لایه واسط تعریف شده اند . این لایه واسط GenericDataLayer میباشد . اگر قبلاً با Ado.net کار کرده باشید حتماً واسط SqlHelper رو بخاطر دارید که روالهای مانند ExecuteDataTable رو در اختیار برنامه نویس قرار میدادند . برای دسترسی این واسط بایستی از این آدرس (http://www.codeplex.com/MultiTierLinqToSql) استفاده کنید که آدرس مرجع اصلی این مقاله هم میباشد . در این تصویر لایه مورد نظر نمایش داده شده است .
استفاده از Generic به این معناست که من نیاز به نوشتن هیچ کدی در کلاس ProductRepository خودم برای انجام عملیات استاندارد CRUD ندارم و این ایمن ترین روش میباشد .
کلاس ProductRepository من امکان بهینه شدن را نیز دارد . این بدان معناست که من میتوانم در این کلاس روالهای اضافی مورد نیازم رو وارد کنم . مثلاً در مثال زیر ما یک Product رو براساس ID جستجو میکنیم . این کار با استفاده از کلمه کلیدی Partial امکان پذیر هست که به ما این امکان رو میده که به سادگی یک کلاس را به ماژولهای مختلف بشکنیم و در زمان کامپایل یک کلاس واحد داشته باشیم .
http://arjmandi.com/image.axd?picture=2009%2f11%2fgeneric_data_layer.p ng
namespace DataLayer{
public partial class ProductStaticRepository
{
public static IList GetByCategory(int id)
{
IQueryable res = SelectAll().Where(p => p.CategoryID == id);
return res.ToList();
}
public static void InsertBlank()
{
Product p = new Product { CategoryID = 1,
ProductName = "test", SupplierID = 1 };
Insert(p);
}
}
}
یکی از امکانات لینک امکان کنترل خطاها در این سطح میباشد . این خطاها بصورت رخدادهایی هستند که لینک به شما امکان پیاده سازی آنها را میدهد . البته من شخصاً سعی میکنم کاربر اطلاعات غلط وارد فرم اطلاعات نکند اما این امکان به نوعی مهم کاری محسوب میشود . کد زیر نمونه پیاده سازی رخداد OnProductNameChanging میباشد . بازهم گزینه partial امکان اتصال کلاسها رو بهم فراهم میکنه . بدین ترتیب میشه سایر رخدادها رو پیاده سازی کرد .
using System;
namespace DataLayer
{
public partial class Product
{
partial void OnProductNameChanging(string value)
{
if (value == "")
throw new Exception("Product name cannot be empty");
}
}
}
T4 Template همانطور که قبلاً گفتم برای ایجاد کلاسهای مورد نیاز از یک الگو استفاده میکنیم . برای این کار نیاز به دو فایل DataClasses.tt و L2ST4.ttinclude داریم . روش کار بصورت زیر میباشد :
ایجاد یک پروژه از نوع Class Library برای لایه داده ها
http://arjmandi.com/image.axd?picture=2009%2f11%2fclass-library.png
ایجاد یک مخزن داده لینک (DataContext) به پروژه و تغییر نام آن به DataClasses.dbml . (باید نام این فایل با نام فایل الگو یکسان باشد ) . بعد از آن اضافه کردن جداول بانک اطلاعاتی مورد نظر به لینک .
http://arjmandi.com/image.axd?picture=2009%2f11%2fdatacontext.png
اضافه کردن دو فایل الگو به پروژه . در این لحظه باید پیام اخطار زیر بروی صفحه ظاهر شود که مربوط به تولید خودکار کد توسط فایل الگو میباشد . با زدن دکمه OK فایل مورد نظر تولید میشود .
http://arjmandi.com/image.axd?picture=2009%2f11%2falert.png
در این مرحله باید کتابخانه GenericDataLayer به پروژه اضافه شود و فایل DataClasses.generated.cs که توسط MSLinqToSQLGenerator تولید شده و در کنار فایل DataClasses.dbml.layout در زیر DataClasses.dbml حذف شود تا فایلی که توسط T4 Template ایجاد شده جانشین آن شود . اکنون میتوانید پروژه را کامپایل نمایید .
http://arjmandi.com/image.axd?picture=2009%2f11%2fsolution.png
ادامه مطالب در پست بعدی