شروع کار با Fluent NHibernate با یک نمونه کد
اگر Fluent NHibernate را downdoad کردهاید آنرا اجرا کنید. وگر نه با استفاده از این لینک آن را دانلود کنید.
در این مثال ما یک domain ساده را برای یک کمپانی خردهفروشی در نظرگرفتهایم. فروشگاه کارمند و کالا دارد که هر کدام با یکدیگر ارتباط دارند. برای روشن شدن مسئله شکل زیر را ملاحظه کنید.
ابتدا یک console Application ایجاد کنید و fluent NHibernate.dll را به آن ارجاع دهید. و هر کدام از نسخههای NHibernate را که تمایل داشتید در آن قرار دهید. برای این مثال ما از دیتابیس SQLite استفاده میکنیم. همچنین شما به کتابخانهٔ System.Data.SQLite که با fluent NHibernate ساخته شده است نیز نیاز دارید.
شما میتوانید ساختار پروژه را در شکل روبرو ببینید. پوشهٔ Entities بخش واقعی آبجکتهاست و پوشهٔ mappings محل قرارگیری کلاسهای mapping شما است.
پوشه Enteties :
اجازه دهید با ساخت entity ها شروع کنیم. ما سه جدول داریم که با هم ارتباط دارند و باید مپ شوند. برای هر کدام، کلاسی جداگانه و مخصوص به خود را در پوشهٔ Enteties میسازیم.
ابتدا با کلاس employee کارمان را آغاز میکنیم.
public class Employee
{
public virtual int Id { get; private set; }
public virtual string FirstName { get; set;}
public virtual string LastName { get; set; }
public virtual Store Store { get; set; }
}
همانطور که ملاحظه میکنید به ازای تمام فیلدهای جدول موجود در پایگاه داده، در این فایل یک صفت (property) داریم.
اگر شما با NHibernate ناآشنا هستید باید بدانید که دو چیز مهم و برجسته در اینجا وجود دارد. اول اینکه مشخصهٔ id به صورت private ، set شده است و این به این خاطر است که فقط NHibernate است که میتواند این مقدار را set کند. دوم اینکه همهٔ property ها virtual هستند، بخاطر اینکه NHibernate در حین اجرا از موجودیتها proxy هایی میسازد که اجازه میدهد Lazy load باشد، یعنی اینکه قابلیت override را برای property ها داشته باشد.
دو کلاس دیگر را هم به صورت زیر میسازیم. (توجه کنید که به ازای فیلدهای جدول خودشان و با Type های صحیح اقدام به ایجاد صفت کنید، درغیر این صورت شما با مشکل روبرو خواهید شد. )
پیاده سازی کلاس محصول
public class Product
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual double Price { get; set; }
public virtual IList<Store> StoresStockedIn { get; private set;{
public Product() //constructor
{
StoresStockedIn = new List<Store>();
}
}
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
<pre>
پیاده سازی کلاس خردهفروشی (مغازه)
</pre>
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}
public virtual void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}
متدهای add employee و add product کمی به سادهتر شددن کد ما کمک میکنند. این متدها برای اضافه کردن یک item به collection و تنظیم کردن سمت دیگر رابطه استفاده میشوند. (برای اضافه کردن کالا و کارمند به فروشگاه)
پوشه Mappings :
حالا که entity ها را ساختیم وقت آن رسیده که آنها را با استفاده از fluent NHibernate مپ کنیم. با یک کلاس ساده مثل employee شروع میکنیم. Mapper های زیر باید در پوشهٔ mappings ساخته شوند.
برای مپ کردن هر entity شما باید یک کلاس مپ اختصاصی برایش ایجاد کنید. این کلاس مپینگها از ClassMapp<T> به ارث میروند و T ، درواقع نام entity ی ورودی شما است.
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Store);
}
}
ستون id یک شناسه برای fluent NHibernate است و x مثالی از یک نمونه از employee است که fluent NHibernate از آن برای بدست آوردن جزئیات property ها استفاده میکند.
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id);
Map(x => x.Name);
HasMany(x => x.Staff).Inverse().Cascade.All();
HasManyToMany(x => x.Products).Cascade.All().WithTableName("StoreProduct");
}
}
همان طور که ملاحضه میکنید در اینجا فراخوانهای دیگری نیز به چشم میخورد. اگر employee را بخاطر بیاورید در آنجا یک ارتباط یک به چند با store داشتیم. حالا هم در مپ کردن store باید این ارتباط را نیز در نظر بگیریم. HasMany یک ارتباط یک به چند را با employee برقرار میکند. در اینجا یک متد جدید HasManyToMany وجود دارد که ارتباط چند به چند با product را ایجاد مینماید.
Inverse در HasMany یک عبارت (کلمه کلیدی) در NHibernate است و در پایان ارتباط مسئولیت save را به عهده دارد.
CascadeAll در HasManyToMany به NHibernate میگوید که در سلسلهٔ آبشاری عمل save را انجام دهد.
WithTableName نام طرف دیگر ارتباط را در چند به چند مشخص میکند. برای سایر ارتباطات به وجود این قسمت نیاز نمیباشد.
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id);
Map(x => x.Name);
Map(x => x.Price);
HasManyToMany(x => x.StoresStockedIn).Cascade.All().Inverse().WithTableName("StoreProduct");
}
}
Application :
در این بخش data ها مقداردهی اولیه میشوند.
static void Main()
{
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
// create a couple of Stores each with some Products and Employees
var barginBasin = new Store { Name = "Bargin Basin" };
var superMart = new Store { Name = "SuperMart" };
var potatoes = new Product { Name = "Potatoes", Price = 3.60 };
var fish = new Product { Name = "Fish", Price = 4.49 };
var milk = new Product { Name = "Milk", Price = 0.79 };
var bread = new Product { Name = "Bread", Price = 1.29 };
var cheese = new Product { Name = "Cheese", Price = 2.10 };
var waffles = new Product { Name = "Waffles", Price = 2.41 };
var daisy = new Employee { FirstName = "Daisy", LastName = "Harrison" };
var jack = new Employee { FirstName = "Jack", LastName = "Torrance" };
var sue = new Employee { FirstName = "Sue", LastName = "Walkters" };
var bill = new Employee { FirstName = "Bill", LastName = "Taft" };
var joan = new Employee { FirstName = "Joan", LastName = "Pope" };
// add products to the stores, there's some crossover in the products in each
// store, because the store-product relationship is many-to-many
AddProductsToStore(barginBasin, potatoes, fish, milk, bread, cheese);
AddProductsToStore(superMart, bread, cheese, waffles);
// add employees to the stores, this relationship is a one-to-many, so one
// employee can only work at one store at a time
AddEmployeesToStore(barginBasin, daisy, jack, sue);
AddEmployeesToStore(superMart, bill, joan);
// save both stores, this saves everything else via cascading
session.SaveOrUpdate(barginBasin);
session.SaveOrUpdate(superMart);
transaction.Commit();
}
// retreive all stores and display them
using (session.BeginTransaction())
{
var stores = session.CreateCriteria(typeof(Store))
.List<Store>();
foreach (var store in stores)
{
WriteStorePretty(store);
}
}
Console.ReadKey();
}
}
public static void AddProductToStore(Store store, params Product[] products)
{
foreach (var product in products)
{
store.AddProduct(product);
}
}
public static void AddEmployeeToStore(Store store, params Employee[] employees)
{
foreach (var employee in employees)
{
store.AddEmployee(employee);
}
}
اما شما الان قادر به run کردن برنامه نیستید زیرا ما نیاز داریم متد CreateSessionFactory را پیاده سازی کنیم.
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
SQLiteConfiguration.Standard
.UsingFile("firstProject.db")
)
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<Program>())
.BuildSessionFactory();
}
اگر شما برای برنامه نمیخواهید که Schema را دستی تهیه کنید، اجرای دفعهٔ اول fail میشود. و آن به این خاطر است که یک چیز دیگر در آبجکت NHibernate configuration نیاز دارید و آن متد expose configuration است . با استفاده از آن شما قادرید در زمان اجرا (Run Time) Schema را بسازید. (اگر مایلید که این کار را انجام دهید) کد را به صورت زیر تغییر بدهید.
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(
SQLiteConfiguration.Standard
.UsingFile("firstProject.db")
)
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<Program>())
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
}
private static void BuildSchema(Configuration config)
{
// delete the existing db on each run
if (File.Exists(DbFile))
File.Delete(DbFile);
// this NHibernate tool takes a configuration (with mapping info in)
// and exports a database schema from it
new SchemaExport(config)
.Create(false, true);
}
با استفاده از کد بالا Schema شما بصورت RunTime ایجاد میشود و شما لازم نیست که بصورت دستی Schema را ایجاد نمایید.
شما میتوانید نمونههای بیشتری از fluent configuration را در این لینک مشاهده کنید.
لینکهای استفاده شده برای تهیهٔ این مطلب :
http://wiki.fluentnhibernate.org/show/HomePage
http://wiki.fluentnhibernate.org/show/GettingStarted:+First+Project
http://wiki.fluentnhibernate.org/show/FluentConfiguration
Fluent NHibernate چیست؟

Fluent NHibernate Logo
Fluent NHibernate نیز مانند NHibernate یک برنامۀ open source است که برای map کردن کلاس ها به جداول database مورد استفاده قرار میگیرد.
FluentNHibernate پیشنهاد میکندکه بجای نوشتن کد xml تحت عنوان (.hbm.xml files) از کدC# استفاده کنید.این باعث میشود کدنویسی سادهتر شده و قابلیت خواندن کدها بهبود یابد و البته کد کوتاهتر شود.
همانطور که میدانید در استفاده از NHibernate به عنوان یک OR Mapper برای مپ کردن کلاسها به جداول از فایلهای xml استفاده میشود.اما Fluent NHibernate چیز دیگری را پیشنهاد میکند.اما چرا؟
فایل xml خوب است اما چند مورد نامطلوب در مورد آن وجود دارد:
- به علت اینکه xml بوسیلهٔ کامپایلر ارزیابی نمیشود،شما میتوانید در کلاسهایتان نام property ها را تغییر دهید ولی آنها در فایلهای mapping ، update نمیشوند. و شما این تغییر را تا زمان اجرا متوجه نمیشوید.
- فایل xml زیادی طولانی است.گرچه NHibernate به تدریج از element های اجباریxml کاسته اما شما هنوز از این درازنویسی رها نشدهاید.
- Mapping های تکراری-فایلهای hbm در NHibernate میتوانند کاملاً طولانی شوند اگر شما برای هر یک قوانینی تعین کنید.مثل اینکه بخواهید همهٔ string ها not-null باشند.
حالا Fluent NHibernate چگونه با این مسائل برخورد میکند:
میتوانید mapping هاتان را به سمت کد واقعی حرکت دهید پس آنها در تمام application کامپایل میشوند و اگر تغییراتی را اعمال کنید کامپایلر در لحظه اخطار میدهد و یا با refactory تغییرات را اعمال میکند. و برای بازنویسی قوانین نحوهای را پیشنهاد میکند که یک بار این قوانین نوشته شوند.
به نمونه کد زیر توجه کنید.
public class CatMap : ClassMap
{
public CatMap()
{
Id(x => x.Id);
Map(x => x.Name).WithLengthOf(16).Not.Nullable();
Map(x => x.Sex);
References(x => x.Mate);
HasMany(x => x.Kittens);
}
}
Fluent NHibernate را میتوانید از سایت رسمیاش به این آدرس
و یا از سرورهای گوگل کد در این آدرس دریافت نمایید.
NHibernate 2.1 عرضه شد
همانطور که میدانید دیروز یعنی در مورخهٔ 2009/25/7 ورژن 2.1 ، NHibernate عرضه شد. در صورت تمایل میتوانید از این لینک برای Download استفاده کنید.
اگر شما تا حالا با NHibernate 2.0 کار میکردید و حالا میخواهید آن را به نسخه جدید upgrade کنید. اگر برای این کار فقط فایلهای dll را تغییر دهید و برنامه را اجرا کنید به error زیر برمیخورید.
The ProxyFactoryFactory was not configured.
برای رفع این مشکل باید صفت ‘proxyfactory.factory_class’ را از بخش session-factory در Web.Config برنامه با یک NHibernate.ByteCode مقدار دهی اولیه کنید.
برای این کار به روش زیر عمل کنید:
فایل Web.Config را باز کرده
بخش مربوط به NHibernate را در آن پیدا کنید و صفت زیر را به آن اضافه کنید.
<property name=’proxyfactory.factory_class’>NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
همچنین میتوانید بصورت زیر نیز عمل کنید:
<property name=’proxyfactory.factory_class’>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
همانطور که ملاحظه کردید . پیش از این NHibernate بطور پیش فرض از Castles Dynamic proxy برای generate ، proxy های runtime استفاده میکرد اما در نسخه جدید شما میتوانید از هر نمونه دلخواهی استفاده کنید و به همین دلیل باید آن را در تنظیمات برنامه اضافه کنید.
<property name=»proxyfactory.factory_class»>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
اگر هم میخواهید از proxy factory دیگری استفاده کنید، دقت کنید که آن را به صورت صحیح اضافه کنید.
لینکهای مرتبط و منابع
ساخت، load و ذخیره کردن آبجکتها در NHibernate
ITransaction tx;
try
}
session = factory.OpenSession();
tx = session.BeginTransaction();
// do database work
tx.Commit();
session.Close();
{
catch (Exception ex(
}
tx.Rollback();
session.Close();
// further exception handling
{
نویسندگان مستندات Hibernate مصرانه پیشنهاد میکنند که هرگز یک exception را بازیابی نکنید زیرا بازیابی تراکنش به عنوان اولین قدم در handle کردن Exception ها در ابتدای برنامه قرار دارند. این خیلی مهم است زیرا NHibernate به صورت آبشاری مدیریت می کند.
جایی که به مشکل بخورند خودشان را نشان میدهند. بهترین نظر این است که همه چیز را به حالت اول برگردانید، در دامنه مدلتان مشکل را حل کنید و دوباره سعی کنید.
بگذارید راجع به انواع رایج فعالیتها برای object های ماندگارمان صحبت کنیم. خب، دیدید که پیادهسازی سه متد اول interface DBMgr در بالا نمایش داده شد.متد getDepartments() لیستی از بخش های ِDepartment را برمی گرداند.
{
IList depts = null;
try
{
ISession session = factory.OpenSession();
ITransaction tx = session.BeginTransaction();
depts = session.CreateCriteria(typeof(Department)).List();
session.Close();
}
catch (Exception ex)
{
tx.Rollback();
session.Close();
// handle exception.
}
return depts;
}
آبجکت session نشان میدهد متد createcriteria() که به عنوان آرگومان، یک نوع کلاس پایدار را میگیرد. این نشان میدهد که NHibernate با تبدیل های مپ شده به آبجکت ها فعل و انفعالها را تندتر میکند. در نتیجه list() نتایج برگشتی از تمامی interface های موجود را برمیگرداند. در این مورد بهتر است یک لیست شامل خواصی از Department را برگردانیم.که با دیتابیس کاملا یکی است (شامل تمام دیگر آبجکت های مرتبط با department). این متد صریحی است که استفاده میشود که در دیتابیس شما در میان تمام interface های کلاس تکرار میشود.
اگر شما می خواهید interface مشخصی از database را تکرار کنید شما باید مقادیر مشخص از شناسه فیلدها را برای کلاس هدف بدانید که شما دنبال آن هستید.
{
Department d = null;
try
{
ISession session = factory.OpenSession();
ITransaction tx = session.BeginTransaction();
d = (Department)session.Load(typeof(nhRegistration.Department), i);
session.Close();
}
catch (Exception ex)
{
tx.Rollback();
session.Close();
// handle exception. }
return d;
}
فقط به خاطر داشته باشید که نتایج session.load() را به یک نوع مناسب object ای که برمیگرداند cast کنید.
در پایان آیا میخواهید تغییرات را در یک interface ذخیره کنید (یا یک interface جدید ایجاد کنید)؟ آیا session در متد برای شما session.save() را که برای ایجاد یک interface جدید به کار میرود در نظر گرفته است یا session update() که برای تغییر یک interface از session موجود به کار می رود؟ شما مجبورید که این دو را جدا در نظر بگیرید. اگر شما یک متد اشتباه را اجراکنید. تداخل session و خطاهای پایگاه داده رخ میدهد ، در interface ما با وجود اینکه پیادهسازی DBmgr فقط یک متد saveDepartment(Department dept) را دارد. این برای یک برنامۀ ساده عقلانی نیست که برنامه نویسان مجبور شوند بین ماندگار کردن یک object جدید و یک object از پیش موجود تفاوت قایل شوند به همین دلیل interface ما فقط یک متد دارد. در پیادهسازی ما هنوز نیازمند این تفلوت قایل شدن هستیم.
{
try
{
ISession session = factory.OpenSession();
ITransaction tx = session.BeginTransaction();if(dept.Id == 0)
{
session.Save(dept);
}
else
{
session.Update(dept);
}
tx.Commit();
session.Close();
}
catch (Exception ex)
{
tx.Rollback();
session.Close();
// handle exception
}
}
ابتدا پیش از آنکه interface را ماندگار کنیم باید این را تشخیص دهیم که آیا از پیش وجود دارد یا اینکه جدید است. بوسیلۀ یک فیلد ساده تشخیص هویت integer ی، ما فقط ارزیابی میکنیم که مقدار آن صفر است یا نه. که این معنی را میدهد که هیچ ردیفی از قبل در پایگاه داده وجود ندارد. (صفت ثبت نشده را در بالا به یاد بیاورید). شما میتوانید تصور کنید که این منطق، کاربردی در تشخیص هویتهای پیچیده ندارد، هنگامیکه ما در مورد از پیش وجود داشتن interface تصمیمگیری میکنیم، متد جدیدی را در session برای این کار در نظر میگیریم.
پیاده سازی و پیکربندی NHibernate (با یک نمونه)
اهداف:
1. ایجاد فایل mapper
3. ایجاد رابطه یک به چند در فایلهای مپر ان.هایبرنیت
4. ایجاد رابطه چند به چند در فایلهای مپر ان.هایبرنیت
5. ایجاد زیرکلاس در فایلهای مپر
University
Professor
Student
که ارتباطاتشان به صورت زیر تعریف شده است :

تمرین یک : روابط بین کلاسها
توضیحات :
یک دانشگاه دارای یک یا چند استاد است و یک استاد در یک یا چند دانشگاه مشغول به کار است، یک دانشگاه یک یا چند کلاس دارد و کلاسها متعلق به یک دانشگاه هستند ، کلاس را یک استاد اداره میکند و هر استاد میتواند چند کلاس را اداره کند ، دانشجویان میتوانند یک یا چند کلاس داشته باشند و هر کلاس میتواند یک یا چند دانشجو داشته باشد .
تعریف کلاسها به صورت زیر میباشد :
{
private int id;
private string name;
private IDictionary classes;
private IDictionary professors;
}
{
private int id;
private string firstname;
private string lastname;
private string id;
private IDictionary departments;
private IDictionary classes;
}
public class UniversityClass
{
private int id;
private string name;
private string number;
private string syllabus;
private DateTime startDate;
private Professor professor;
private IDictionary students;
private Department department;
}
public class Student : Person
{
private int id;
private string firstname;
private string lastname;
private string ssn;
private IDictionary classes;
}
public interface Person
{
public int Id;
public string FirstName;
public string LastName;
}

تمرین یک : روابط بین جداول پایگاه داده
فایلهای map شده
مرحلهٔ بعد فراهم کردن فایلهای مپ شده است که با اطلاعات موجود در جداول در ارتباط هستند. هر کلاس، فایلهای مپ شدهٔ خود را نیاز دارد. این فایلها میتوانند هرجا که شما بخواهید ذخیره شوند. اما بهتر است برای سادگی کار یک library به پروژه add کنید و این فایلها را در آنجا ذخیره کنید. توجه داشته باشید که فایلهای مپ شده برای ویژگیهایی که قرار است ماندگار (Persistence) باشند (ذخیره شوند) تهیه میشوند.
<?xml version=«1.0« encoding=«utf-8« ?>
<hibernate-mapping xmlns=«urn:nhibernate-mapping-2.0«>
حالا برای mapping مراحل زیر را طی میکنیم:
در ابتدا مشخص میکنیم که کدام کلاس را مپ میکنیم. این کار را با نوشتن کلمهٔ class و نسبت دادن یک نام به آن انجام میدهیم.
<!–[if gte mso 9]> Normal 0 false false false EN-US X-NONE AR-SA <![endif]–><!–[if gte mso 9]> <![endif]–> <!–[endif]–>
<class name=«nhRegistration.Department, nhRegistration» table=«Department« />
حالا مشخصهها را به یک فیلد DataBase مپ میکنیم. برای این کار باید نام مشخصه، ستون و نوع آن بیان شود. توجه داشته باشید که مقدار name در برنامه قابل دسترسی است و مقدار column باید و باید مشابه نام ستونی باشد که در جدول پایگاه داده ایجاد کردهاید. با این کار میتوان نامهای متفاوتی را برای ستونهای جداول در پایگاه داده و آنچه که در عمل با آن در حال کار هستید، ایجاد کرد. فیلد type هم بیانگر نوع فیلد شما در جدول پایگاه داده است.
<!–[if gte mso 9]> Normal 0 false false false EN-US X-NONE AR-SA <![endif]–><!–[if gte mso 9]> <![endif]–><!–[endif]–>
<property name=«Name« column=«DeptName« type=«String(50)«/>
مورد جالب در اینجا فیلد id از کلاس Department است. هر کلاس باید فیلدی داشته باشد که شامل دادهای باشد که یکتاست. در مثال ما هر کلاس یک شناسه id دارد که یکتاست. در اینجا شما باید یک مجموعهٔ استاندارد از صفات را فراهم کنید که مهمترینشان Generator نام دارد و مشخص میکند که این داده چطور درست شده (توسط برنامهنویس یا بوسیله NHibernate یا توسط لایههای پایینتر).
Applicationهای مختلف قوانین مختلفی دربارهٔ شناسهها دارند و پایگاههایداده مختلف سرویسهای unique ای را برای مدیریت مقادیر پیشنهاد میکنند. در اینجا به چند مورد اشاره میکنیم.
مقادیر رایج برای generator ها:
1. Identity
2. Sequence
3. Hilo : استفاده از الگوریتم hilo برای generate کردن مقادیر شناسهها.
4. Native : انتخاب خود DataBase
شناسهٔ unsaved_value یک مقدار default برای مشخصهٔ id است. یعنی وقتی که یک object ساخته شده ولی هنوز ذخیره نشده.
در اینجا ما native را انتخاب میکنیم. تا آنچه را که در پایگاه داده تعیین کردهایم به عنوان id قرار بگیرد.
<id name=«Id« column=«DeptID« type=«Int32«>
<generator class=«assigned« />
</id>
حالا کد ما به این شکل درآمده:
<?xml version=«1.0« encoding=«utf-8« ?>
<hibernate-mapping xmlns=«urn:nhibernate-mapping-2.0«>
<class name=«nhRegistration.Department, nhRegistration» table=«Department«>
<id name=«Id« column=«DeptID« type=«Int32«>
<generator class=«assigned« />
</id>
<property name=«Name« column=«DeptName« type=«String(50)«/>
</class>
</hibernate-mapping>
سپس به جمعآوری property ها میپردازیم (هر کدام که بخصوص باشند). بیایید به اولین کلاسمان نگاهی بیاندازیم که مجموعهای از کلاسها در دانشگاه است. نیازها و مدلها را به خاطر داریم: یک دانشگاه میتواند چندین کلاس داشته باشد اما کلاس متعلق به یک دانشگاه است. این یک ارتباط یک به چند است. برای مدل کردن آن نیاز به element های <set> داریم که نشان دهیم که مجموعهای را مپ میکنیم.
<set name=«Classes« cascade=«all«>
<key column=«DeptID«/>
<one-to-many class=«nhRegistration.UniversityClass,nhRegistration«/>
</set>
به عبارت سادهتر با کد بالا، مقادیر کلید DeptID که در جدول خودش PrimaryKey است، به ستون مشخص شدهاش Map میشود. نشانهٔ name در <set> نام یک فیلد است که مجموعه را نگه میدارد. در اینجا column در <key> نام یک ستون در مجموعهای است که به مشخصهٔ id کلاس والد مپ میشود و <one_to_many> نوع کلاس گردآوری شده است. به این معنی که رابطه این ستون (فیلد) با ستون دیگر به چه صورتی است ( پایینتر به انواع دیگری هم اشاره میکنیم)
بنابراین جدولuniversity یک فیلد به نام deptid دارد که به id در کلاس university ی ما مپ میشود.
سرانجام ما باید فیلد professors را مپ کنیم. مدلمان را به خاطر بیاورید، دانشگاه میتواند چندین استاد داشته باشد و اساتید نیز میتوانند در چندین دانشگاه مشغول به کار باشند. این یک ارتباط چند به چند است. که در زیر نمونهای از آن را مشاهده میکنید، در پیاده سازی صرفا کلمه کلیدی one-to-many و many-to-many تفاوت میکند.
<set name=«Professors« table=«departmentprofessor«>
<key column=«DeptID«/>
<many-to-many class=«nhRegistration.Person,nhRegistration» column=«Personid«/>
</set>
حال مپینگ کامل زیر را بخوانید
<?xml version=«1.0« encoding=«utf-8« ?>
<hibernate-mapping xmlns=«urn:nhibernate-mapping-2.0«>
<class name=«nhRegistration.Department, nhRegistration«
table=«Department«>
<id name=«Id« column=«DeptID« type=«Int32«>
<generator class=«assigned« />
</id>
<property name=«Name« column=«DeptName« type=«String(50)«/>
<set name=«Classes« cascade=«all«>
<key column=«DeptID«/>
<one-to-many class=«nhRegistration.UniversityClass,nhRegistration«/>
</set>
<set name=«Professors« table=«departmentprofessor«>
<key column=«DeptID«/>
<many-to-many class=«nhRegistration.Person,nhRegistration« column=«Personid«/>
</set>
</class>
</hibernate-mapping>
اگر ما با استفاده از این مپینگها سعی کنیم که پروژه را load کنیم، موفق نمیشویم. NHibernate یک exception را گزارش میدهد زیرا ارتباطات یک به چند و چند به چند داریم. وقتی ارتباطاتی شبیه این داریم که NH هر دو طرف آن است باید نوع آنها را هم مپ کنیم.
<!–[if gte mso 9]> Normal 0 false false false EN-US X-NONE AR-SA <![endif]–><!–[if gte mso 9]> <![endif]–><!–[endif]–>
<?xml version=«1.0« encoding=«utf-8« ?>
<hibernate-mapping xmlns=«urn:nhibernate-mapping-2.0«>
<class name=«nhRegistration.UniversityClass, nhRegistration«
table=«UniversityClass«>
<id name=«Id« column=«DeptId« type=«Int32«>
<generator class=«assigned« />
</id>
<property name=«Name« column=«ClassName» type=«String(50)«/>
<many-to-one name=«Dept« class=«nhRegistration.Department,nhRegistration« column=«DeptID«/>
</class>
</hibernate-mapping>
تنها element جدید در این فایل <many_to_one> است که نمایندهٔ طرف دیگر <one_to_many> در Department.hbm.xml است.
نوبت به professor میرسد که از همه جالبتر است، زیرا هم از interface ای بنام person به ارث رفته و هم یک جدول مشترک با دانشجو دارد. برای تشخیص professor و student باید به NH بگوییم که نوع موجودیت چیست و در <discriminator> آن را مشخص میکنیم.
اگر نگاهی به مدل بیاندازید person ،3 فیلد را مشخص میکند: id ، person و lastname. student و professor هر دو این نمایندهٔ این فیلدها هستند اما آنه اتفاوت دارند. Student ، ssn دارد در حالی که professor ، identifier دارد. Professor مجموعهای از دانشگاهها و کلاسها را دارد در حالی که student فقط مجموعهای از کلاسها (ی درس) را دارد. شما میتوانید فیلدها را در کلاس person مپ کنید اما فیلدهایی را که خاص هستند به عنوان subtype در <subclass> مپ کنید.
در حقیقت برای ثبت فضا، مجموعه مختلفی از فایلهای مپ شده را برای پررنگ کردن featureهای polymorphism related ادغام میکنیم. این مجموعه به نظر میرسد شبیه مثالهای بالا باشد.
<?xml version=«1.0« encoding=«utf-8« ?>
<hibernate-mapping xmlns=«urn:nhibernate-mapping-2.0«>
<class name=«nhRegistration.Person, nhRegistration« table=«People«>
<id name=«Id« column=«PersonID« type=«Int32«>
<generator class=«assigned« />
</id>
<discriminator column=«PersonType« type=«String«/>
<property name=«FirstName« column=«FirstName» type=«String(50)«/>
<property name=«LastName« column=«LastName» type=«String(50)«/>
<subclass name=«nhRegistration.Professor, nhRegistration» discriminator-value=«Professor«>
<property name=«Identifier« column=«Identifier» type=«String«/>
</subclass>
<subclass name=«nhRegistration.Student, nhRegistration» discriminator-value=«Student«>
<property name=«SSN« column=«Identifier» type=«String«/>
</subclass>
</class>
</hibernate-mapping>
با انجام کارهای بالا شما یک پروژه را با موفقیت به پایگاه داده مپ کردهاید. در مطالب بعدی نحوه استفاده از این کلاسها را مرور خواهیم کرد.
منابعی که در نگارش این مطلب بکار رفته است.
نصب و راه اندازی NHibernate
برای استفاده از NHibernate مراحل مخنلفی را باید سپری کرد که در زیر به آنها اشاره میشود. توجه داشته باشید که برای استفاده از ان.هایبرنیت باید کارهای دیگری انجام داد که در آینده به آنها خواهیم پرداخت، در این مطلب قصد داریم نحوه نصب و پیکربندی NHibernate را در یک سیستم تشریح کنیم .
گام اول . دانلود و نصب ان.هایبرنیت .
در ابتدا NHibernate را از اینجا در سورس فورگ دانلود کزده و نصب کنید.
گام دوم . اضافه کردن DLL های ان.هایبرنیت به پروژه.
پروژهای ایجاد کنید و فایل اسمبلی داخل پوشه BIN را که از آدرس بالا دانلود کردید به پروژه اضافه کنید.
گام سوم . تنظیمات مورد نیاز در Web.Config .
در ادامه تنظیمات زیر در فایل web.config برنامه اضافه میکنیم.
<configuration>
<configSections>
<section name=«nhibernate« type=«System.Configuration.NameValueSectionHandler, System,Version=2.0.1,Culture=neutral,PublicKeyToken=b77a5c561934e089« />
</configSections>
<nhibernate>
<add key=«hibernate.connection.provider« value=«NHibernate.Connection.DriverConnectionProvider«/>
<add key=«hibernate.dialect« value=«NHibernate.Dialect.MsSql2000Dialect«/>
<add key=«hibernate.connection.driver_class« value=«NHibernate.Driver.SqlClientDriver«/>
<add key=«hibernate.connection.connection_string« value=«Server=.;initial catalog=nhibernate;UserID=sa;Password=123;Min Pool Size=2«/>
</nhibernate>
</configuration>
چند نکته که باید در ادامه به آنها توجه داشت.
وقتی برای تشخیص Data Store ، FrameWork تان را پیکربندی کردید، باید فضای مدل (ModelSpace) و همچنین پایگاه دادهتان را معرفی و مشخص کنید.
نکته دیگری هم وجود دارد و آن map کردن فایلها و توضیح روابط (Relations) بین کلاسها و جداول است.
امروزه NHibernate ابزاری را فراهم کرده که میتواند DLL هایی را از فایلهای map شده تولید کند. و اضافه کردن یک شیء جدید به برنامه، درواقع یک NAnt task است که بطور خودکار توسط #C از فایلهای مپ شده تولید میشود.
با انجام تنظیمات بالا باید براحتی از NHibernate در برنامهتان استفاده کنید. در مطلب بعدی یک مثال کاملا عملی برای کار با NHibernate میآورم.
منابعی که برای نگارش این مطلب از آنها استفاده شده است
ویژگیهای NHibernate
از آنجا که برای شروع به کار با یک برنامه، کسب اطلاع از نقاط ضعف و قوت آن از ملزومات است و از آنجا که برنامه NHibernate در یکی از حساس ترین و مهمترین لایه های برنامه نهایی اجرا می شود، دانستن مطالب زیر را الزامی میدانم، همچنین لازم بذکر است که مطالب زیر شامل مباحث فنی مختلفی است که از حوصله این مطلب خارج است به همین دلیل توضیحات مربوط به اصطلاحات ذکر شده را به عهده خواننده مطلب میگذارم.
1. Natural programming model : بدین معنا که NHibernate ، OO را پشتیبانی میکند . یعنی ارثبری (Inheritance) ، چندریختی (Polymorphism) ، ترکیب (Composition) ، Collection در .NET که شامل Generic collection ها هم میشود .
2. Native .net : API های NHibernate بر مبنای زبانهای .NET و استانداردهای آن ایجاد شده است .
3. Support for fine-grained object models : پشتیبانی از مدلهای آبجکتی، از طریق مدلهای مختلف غنی شده از مپینگها، برای Collection ها و Object های وابسته .
4. No build time byte code enhancement : در روند بیلد شدن، پردازش بر اساس بایتکدها و ایحاد کدهای اضافی وحود ندارد . در حقیقت در زمان بیلد شدن هیچ بهبودی در بایتکدها داده نمیشود .
5. The query options : از بدست آوردن آبجکتهای دیتابیس گرفته تا بدست آوردن خروجیهای آنها . به هر دوی این مسائل میپردازد .
6. Custom SQL : تصحیح کامل SQL که NHibernate باید برای آبحکتهایش از آنها استفاده کند .
7. Support for “conversations” : ان.هایبرنیت از ماندگاری بالای مفاهیم ، سوا و سرهمبندی آبحکتها پشتیبانی میکند و همچنین از optimistic locking به صورت خودکار مراقبت میکند .
8. Free/open source : ان هاببرنیت، تحت مجوز LGPL (Lesser GNU Public License) است و سورس پروژه ان.هایبرنیت را از سایت SourceForge از اینجا می توانید دانلود کنید .
منابعی که در نگارش این مطلب از آنها استفاده شده است
سایت رسمی NHibernate
توضیحات ویکی پدیا در مورد NHibernate