۱۳۸۸/۰۷/۱۷

آشنايي با NHibernate - قسمت اول


NHibernate كتابخانه‌ي تبديل شده پروژه بسيار محبوب Hibernate جاوا به سي شارپ است و يكي از ORM هاي بسيار موفق، به شمار مي‌رود. در طي تعدادي مقاله قصد آشنايي با اين فريم ورك را داريم.

چرا نياز است تا از يك ORM استفاده شود؟
تهيه قسمت و يا لايه دسترسي به داده‌ها در يك برنامه عموما تا 30 درصد زمان كل تهيه يك محصول را تشكيل مي‌دهد. اما بايد در نظر داشت كه اين پروسه‌ي تكراري هيچ كار خارق العاده‌اي نبوده و ارزش افزوده‌ي خاصي را به يك برنامه اضافه نمي‌كند. تقريبا تمام برنامه‌هاي تجاري نياز به لايه دسترسي به داده‌ها را دارند. پس چرا ما بايد به ازاي هر پروژه، اين كار تكراري و كسل كننده را بارها و بارها تكرار كنيم؟
هدف NHibernate ، كاستن اين بار از روي شانه‌هاي يك برنامه نويس است. با كمك اين كتابخانه، ديگر رويه ذخيره شده‌اي را نخواهيد نوشت. ديگر هيچگاه با ADO.Net سر و كار نخواهيد داشت. به اين صورت مي‌توان عمده وقت خود را صرف قسمت‌هاي اصلي و طراحي برنامه كرد تا كد نويسي يك لايه تكراري. همچنين عده‌اي از بزرگان اينگونه ابزارها اعتقاد دارند كه برنامه نويس‌هايي كه لايه دسترسي به داده‌ها را خود طراحي مي‌كنند، مشغول كلاهبرداري از مشتري‌هاي خود هستند! (صرف زمان بيشتر براي تهيه يك محصول و همچنين وجود باگ‌هاي احتمالي در لايه دسترسي به داده‌هاي طراحي شده توسط يك برنامه نويس نه چندان حرفه‌اي)
براي مشاهده ساير مزاياي استفاده از يك ORM لطفا به مقاله "5 دليل براي استفاده از يك ابزار ORM" مراجعه نمائيد.

در ادامه براي معرفي اين كتابخانه يك سيستم ثبت سفارشات را با هم مرور خواهيم كرد.

بررسي مدل سيستم ثبت سفارشات

در اين مدل ساده‌ي ما، مشتري‌ها (customers) امكان ثبت سفارشات (orders) را دارند. سفارشات توسط يك كارمند (employee) كه مسؤول ثبت آن‌ها است به سيستم وارد مي‌شود. هر سفارش مي‌تواند شامل يك يا چند (one-to-many) آيتم (order items) باشد و هر آيتم معرف يك محصول (product) است كه قرار است توسط يك مشتري (customer) خريداري شود. كلاس دياگرام اين مدل به صورت زير مي‌تواند باشد.


نگاشت مدل

زمانيكه مدل سيستم مشخص شد، اكنون نياز است تا حالات (داده‌ها) آن‌را در مكاني ذخيره كنيم. عموما اينكار با كمك سيستم‌هاي مديريت پايگاه‌هاي داده مانند SQL Server، Oracle، IBM DB2 ، MySql و امثال آن‌ها صورت مي‌گيرد. زمانيكه از NHibernate استفاده كنيد اهميتي ندارد كه برنامه شما قرار است با چه نوع ديتابيسي كار كند؛ زيرا اين كتابخانه اكثر ديتابيس‌هاي شناخته شده موجود را پشتيباني مي‌كند و برنامه از اين لحاظ مستقل از نوع ديتابيس عمل خواهد كرد و اگر نياز بود روزي بجاي اس كيوال سرور از ماي اس كيوال استفاده شود، تنها كافي است تنظيمات ابتدايي NHibernate را تغيير دهيد (بجاي بازنويسي كل برنامه).
اگر براي ذخيره سازي داده‌ها و حالات سيستم از ديتابيس استفاده كنيم، نياز است تا اشياء مدل خود را به جداول ديتابيس نگاشت نمائيم. اين نگاشت عموما يك به يك نيست (لزومي ندارد كه حتما يك شيء به يك جدول نگاشت شود). در گذشته‌ي نچندان دور كتابخانه‌ي NHibernate ، اين نگاشت عموما توسط فايل‌هاي XML ايي به نام hbm صورت مي‌گرفت. اين روش هنوز هم پشتيباني شده و توسط بسياري از برنامه نويس‌ها بكار گرفته مي‌شود. روش ديگري كه براي تعريف اين نگاشت مرسوم است، مزين سازي اشياء و خواص آن‌ها با يك سري از ويژگي‌ها مي‌باشد كه فريم ورك برتر اين عمليات Castle Active Record نام دارد.
اخيرا كتابخانه‌ي ديگري براي انجام اين نگاشت تهيه شده به نام Fluent NHibernate كه بسيار مورد توجه علاقمندان به اين فريم ورك واقع گرديده است. با كمك كتابخانه‌ي Fluent NHibernate عمليات نگاشت اشياء به جداول، بجاي استفاده از فايل‌هاي XML ، توسط كدهاي برنامه صورت خواهند گرفت. اين مورد مزاياي بسياري را همانند استفاده از يك زبان برنامه نويسي كامل براي تعريف نگاشت‌ها، بررسي خودكار نوع‌هاي داد‌ه‌اي و حتي امكان تعريف منطقي خاص براي قسمت نگاشت برنامه، به همراه خواهد داشت.

آماده سازي سيستم براي استفاده از NHibernate

در ادامه بجاي دريافت پروژه سورس باز NHibernate از سايت سورس فورج، پروژه سورس باز Fluent NHibernate را از سايت گوگل كد دريافت خواهيم كرد كه بر فراز كتابخانه‌ي NHibernate بنا شده است و آن‌را كاملا پوشش مي‌دهد. سورس اين كتابخانه را با checkout مسير زير توسط TortoiseSVN مي‌توان دريافت كرد.





البته احتمالا براي دريافت آن از گوگل كد با توجه به تحريم موجود نياز به پروكسي خواهد بود. براي تنظيم پروكسي در TortoiseSVN به قسمت تنظيمات آن مطابق تصوير ذيل مراجعه كنيد:



همچنين جهت سهولت كار، آخرين نگارش موجود در زمان نگارش اين مقاله را از اين آدرس نيز مي‌توانيد دريافت نمائيد.

پس از دريافت پروژه، باز كردن فايل solution آن در VS‌ و سپس build كل مجموعه، اگر به پوشه‌هاي آن مراجعه نمائيد، فايل‌هاي زير قابل مشاهده هستند:

Nhibernate.dll : اسمبلي فريم ورك NHibernate است.
NHibernate.Linq.dll : اسمبلي پروايدر LINQ to NHibernate مي‌باشد.
FluentNHibernate.dll : اسمبلي فريم ورك Fluent NHibernate است.
Iesi.Collections.dll : يك سري مجموعه‌هاي ويژه مورد استفاده NHibernate را ارائه مي‌دهد.
Log4net.dll : فريم ورك لاگ كردن اطلاعات NHibernate مي‌باشد. (اين فريم ورك نيز جهت عمليات logging بسيار معروف و محبوب است)
Castle.Core.dll : كتابخانه پايه Castle.DynamicProxy2.dll است.
Castle.DynamicProxy2.dll : جهت اعمال lazy loading در فريم ورك NHibernate بكار مي‌رود.
System.Data.SQLite.dll : پروايدر ديتابيس SQLite است.
Nunit.framework.dll : نيز يكي از فريم ورك‌هاي بسيار محبوب آزمون واحد در دات نت فريم ورك است.

براي سادگي مراجعات بعدي، اين فايل‌ها را يافته و در پوشه‌اي به نام lib كپي نمائيد.

برپايي يك پروژه جديد

پس از دريافت Fluent NHibernate ، يك پروژه Class Library جديد را در VS.Net آغاز كنيد (براي مثال به نام NHSample1 ). سپس يك پروژه ديگر را نيز از نوع Class Library به نام UnitTests به اين solution ايجاد شده جهت انجام آزمون‌هاي واحد برنامه اضافه نمائيد.
اكنون به پروژه NHSample1 ، ارجاع هايي را به فايل‌هاي FluentNHibernate.dll و سپس NHibernate.dll در كه پوشه lib ايي كه در قسمت قبل ساختيم، قرار دارند، اضافه نمائيد.



در ادامه يك پوشه جديد به پروژه NHSample1 به نام Domain اضافه كنيد. سپس به اين پوشه، كلاس Customer را اضافه نمائيد:

namespace NHSample1.Domain
{
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string CountryCode { get; set; }
}
}
اكنون نوبت تعريف نگاشت اين شيء است. اين كلاس بايد از كلاس پايه ClassMap مشتق شود. سپس نگاشت‌ها در سازنده‌ي اين كلاس بايد تعريف گردند.

using FluentNHibernate.Mapping;

namespace NHSample1.Domain
{
class CustomerMapping : ClassMap<Customer>
{
}
}
همانطور كه ملاحظه مي‌كنيد، نوع اين كلاس Generic ، همان كلاسي است كه قصد داريم نگاشت مرتبط با آن را تهيه نمائيم. در ادامه تعريف كامل اين كلاس نگاشت را در نظر بگيريد:

using FluentNHibernate.Mapping;

namespace NHSample1.Domain
{
class CustomerMapping : ClassMap<Customer>
{
public CustomerMapping()
{
Not.LazyLoad();
Id(c => c.Id).GeneratedBy.HiLo("1000");
Map(c => c.FirstName).Not.Nullable().Length(50);
Map(c => c.LastName).Not.Nullable().Length(50);
Map(c => c.AddressLine1).Not.Nullable().Length(50);
Map(c => c.AddressLine2).Length(50);
Map(c => c.PostalCode).Not.Nullable().Length(10);
Map(c => c.City).Not.Nullable().Length(50);
Map(c => c.CountryCode).Not.Nullable().Length(2);
}
}
}
به صورت پيش فرض نگاشت‌هاي Fluent NHibernate از نوع lazy load هستند كه در اينجا عكس آن در نظر گرفته شده است.
سپس وضعيت نگاشت تك تك خواص كلاس Customer را مشخص مي‌كنيم. توسط Id(c => c.Id).GeneratedBy.HiLo به سيستم اعلام خواهيم كرد كه فيلد Id از نوع identity است كه از 1000 شروع خواهد شد. مابقي موارد هم بسيار واضح هستند. تمامي خواص كلاس Customer ذكر شده، نال را نمي‌پذيرند (منهاي AddressLine2) و طول آن‌ها نيز مشخص گرديده است.
با كمك Fluent NHibernate ، بحث بررسي نوع‌هاي داده‌اي و همچنين يكي بودن موارد مطرح شده در نگاشت با كلاس اصلي Customer به سادگي توسط كامپايلر بررسي شده و خطاهاي آتي كاهش خواهند يافت.

براي آشنايي بيشتر با lambda expressions مي‌توان به مقاله زير مراجعه كرد:
Step-by-step Introduction to Delegates and Lambda Expressions


ادامه دارد...