۱۳۸۸/۰۷/۲۱

آشنايي با NHibernate - قسمت پنجم


استفاده از LINQ جهت انجام كوئري‌ها توسط NHibernate

نگارش نهايي 1.0 كتابخانه‌ي LINQ to NHibernate اخيرا (حدود سه ماه قبل) منتشر شده است. در اين قسمت قصد داريم با كمك اين كتابخانه، اعمال متداول انجام كوئري‌ها را بر روي ديتابيس قسمت قبل انجام دهيم.
توسط اين نگارش ارائه شده، كليه اعمال قابل انجام با criteria API اين فريم ورك را مي‌توان از طريق LINQ نيز انجام داد (NHibernate براي كار با داده‌ها و جستجوهاي پيشرفته بر روي آن‌ها، HQL : Hibernate Query Language و Criteria API را سال‌ها قبل توسعه داده است).

جهت دريافت پروايدر LINQ مخصوص NHibernate به آدرس زير مراجعه نمائيد:


پس از دريافت آن، به همان برنامه كنسول قسمت قبل، دو ارجاع را بايد افزود:
الف) ارجاعي به اسمبلي NHibernate.Linq.dll
ب) ارجاعي به اسمبلي استاندارد System.Data.Services.dll دات نت فريم ورك سه و نيم

در ابتداي متد Main برنامه قصد داريم تعدادي مشتري را به ديتابيس اضافه نمائيم. به همين منظور متد AddNewCustomers را به كلاس CDbOperations برنامه كنسول قسمت قبل اضافه نمائيد. اين متد ليستي از مشتري‌ها را دريافت كرده و آن‌ها را در طي يك تراكنش به ديتابيس اضافه مي‌كند:

public void AddNewCustomers(params Customer[] customers)
{
using (ISession session = _factory.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
foreach (var data in customers)
session.Save(data);

session.Flush();

transaction.Commit();
}
}
}
در اينجا استفاده از واژه كليدي params سبب مي‌شود كه بجاي تعريف الزامي يك آرايه از نوع مشتري‌ها، بتوانيم تعداد دلخواهي پارامتر از نوع مشتري را به اين متد ارسال كنيم.

پس از افزودن اين ارجاعات، كلاس جديدي را به نام CLinqTest به برنامه كنسول اضافه نمائيد. ساختار كلي اين كلاس كه قصد استفاده از پروايدر LINQ مخصوص NHibernate را دارد بايد به شكل زير باشد (به كلاس پايه NHibernateContext دقت نمائيد):

using System.Collections.Generic;
using System.Linq;
using NHibernate;
using NHibernate.Linq;
using NHSample1.Domain;

namespace ConsoleTestApplication
{
class CLinqTest : NHibernateContext
{ }
}
اكنون پس از مشخص شدن context يا زمينه، نحوه ايجاد يك كوئري ساده LINQ to NHibernate به صورت زير مي‌تواند باشد:

using System.Collections.Generic;
using System.Linq;
using NHibernate;
using NHibernate.Linq;
using NHSample1.Domain;

namespace ConsoleTestApplication
{
class CLinqTest : NHibernateContext
{
ISessionFactory _factory;

public CLinqTest(ISessionFactory factory)
{
_factory = factory;
}

public List<Customer> GetAllCustomers()
{
using (ISession session = _factory.OpenSession())
{
var query = from x in session.Linq<Customer>() select x;
return query.ToList();
}
}
}
}
ابتدا علاوه بر ساير فضاهاي نام مورد نياز، فضاي نام NHibernate.Linq به پروژه افزوده مي‌شود. سپس از extension متدي به نام Linq بر روي اشياء ISession از نوع يكي از موجوديت‌هاي تعريف شده در برنامه در قسمت‌هاي قبل، مي‌توان جهت تهيه كوئري‌هاي Linq مورد نظر بهره برد.
در اين كوئري، ليست تمامي مشتري‌ها بازگشت داده مي‌شود.

سپس جهت استفاده و بررسي آن در متد Main برنامه خواهيم داشت:

static void Main(string[] args)
{
using (ISessionFactory session = Config.CreateSessionFactory(
MsSqlConfiguration
.MsSql2008
.ConnectionString("Data Source=(local);Initial Catalog=HelloNHibernate;Integrated Security = true")
.ShowSql()
))
{

var customer1 = new Customer()
{
FirstName = "Vahid",
LastName = "Nasiri",
AddressLine1 = "Addr1",
AddressLine2 = "Addr2",
PostalCode = "1234",
City = "Tehran",
CountryCode = "IR"
};

var customer2 = new Customer()
{
FirstName = "Ali",
LastName = "Hasani",
AddressLine1 = "Addr..1",
AddressLine2 = "Addr..2",
PostalCode = "4321",
City = "Shiraz",
CountryCode = "IR"
};

var customer3 = new Customer()
{
FirstName = "Mohsen",
LastName = "Shams",
AddressLine1 = "Addr...1",
AddressLine2 = "Addr...2",
PostalCode = "5678",
City = "Ahwaz",
CountryCode = "IR"
};

CDbOperations db = new CDbOperations(session);
db.AddNewCustomers(customer1, customer2, customer3);

CLinqTest lt = new CLinqTest(session);
foreach (Customer customer in lt.GetAllCustomers())
{
Console.WriteLine("Customer: LastName = {0}", customer.LastName);
}
}

Console.WriteLine("Press a key...");
Console.ReadKey();

}
در اين متد ابتدا تعدادي ركورد تعريف و سپس به ديتابيس اضافه شدند. در ادامه ليست تمامي آن‌ها از ديتابيس دريافت و نمايش داده مي‌شود.

مهمترين مزيت استفاده از LINQ در اين نوع كوئري‌ها نسبت به روش‌هاي ديگر، استفاده از كدهاي strongly typed دات نتي تحت نظر كامپايلر است، نسبت به رشته‌هاي معمولي SQL كه كامپايلر كنترلي را بر روي آن‌ها نمي‌تواند داشته باشد (براي مثال اگر نوع يك ستون تغيير كند يا نام آن‌، در حالت استفاده از LINQ بلافاصله يك خطا را از كامپايلر جهت تصحيح مشكلات دريافت خواهيم كرد كه اين مورد در زمان استفاده از يك رشته معمولي صادق نيست). همچنين مزيت فراهم بودن Intellisense را حين نوشتن كوئري‌هايي از اين دست نيز نمي‌توان نديد گرفت.

مثالي ديگر:
ليست تمام مشتري‌هاي شيرازي را نمايش دهيد:
ابتدا متد GetCustomersByCity را به كلاس CLinqTest فوق اضافه مي‌كنيم:

public List<Customer> GetCustomersByCity(string city)
{
using (ISession session = _factory.OpenSession())
{
var query = from x in session.Linq<Customer>()
where x.City == city
select x;
return query.ToList();
}
}
سپس براي استفاده از آن، چند سطر ساده زير به ادامه متد Main اضافه مي‌شوند:

foreach (Customer customer in lt.GetCustomersByCity("Shiraz"))
{
Console.WriteLine("Customer: LastName = {0}", customer.LastName);
}
يكي ديگر از مزاياي استفاده از LINQ to NHibernate ، امكان بكارگيري LINQ بر روي تمامي ديتابيس‌هاي پشتيباني شده توسط NHibernate است؛ براي مثال ماي اس كيوال، اوراكل و ....
ليست كامل ديتابيس‌هاي پشتيباني شده توسط NHibernate را در اين آدرس مي‌توانيد مشاهده نمائيد. (البته به نظر ليست آن، آنچنان هم به روز نيست؛ چون در نگارش آخر NHibernate ، پشتيباني از اس كيوال سرور 2008 هم اضافه شده است)


نكته:
در كوئري‌هاي مثال‌هاي فوق همواره بايد session.Linq<T> را ذكر كرد. اگر علاقمند بوديد شبيه به روشي كه در LINQ to SQL موجود است مثلا db.TableName بجاي session.Linq<T> در كوئري‌ها ذكر گردد، مي‌توان اصلاحاتي را به صورت زير اعمال كرد:
يك كلاس جديد را به نام SampleContext به برنامه كنسول جاري با محتويات زير اضافه نمائيد:

using System.Linq;
using NHibernate;
using NHibernate.Linq;
using NHSample1.Domain;

namespace ConsoleTestApplication
{
class SampleContext : NHibernateContext
{
public SampleContext(ISession session)
: base(session)
{ }

public IOrderedQueryable<Customer> Customers
{
get { return Session.Linq<Customer>(); }
}

public IOrderedQueryable<Employee> Employees
{
get { return Session.Linq<Employee>(); }
}

public IOrderedQueryable<Order> Orders
{
get { return Session.Linq<Order>(); }
}

public IOrderedQueryable<OrderItem> OrderItems
{
get { return Session.Linq<OrderItem>(); }
}

public IOrderedQueryable<Product> Products
{
get { return Session.Linq<Product>(); }
}
}
}
در اين كلاس به ازاي تمام موجوديت‌هاي تعريف شده در پوشه domain برنامه اصلي خود (همان NHSample1 قسمت‌هاي اول و دوم)، يك متد از نوع IOrderedQueryable را بايد تشكيل دهيم كه پياده سازي آن‌را ملاحظه مي‌نمائيد.
سپس بازنويسي متد GetCustomersByCity بر اساس SampleContext فوق به صورت زير خواهد بود كه به كوئري‌هاي LINQ to SQL بسيار شبيه است:

using System.Collections.Generic;
using System.Linq;
using NHibernate;
using NHSample1.Domain;

namespace ConsoleTestApplication
{
class CSampleContextTest
{
ISessionFactory _factory;

public CSampleContextTest(ISessionFactory factory)
{
_factory = factory;
}

public List<Customer> GetCustomersByCity(string city)
{
using (ISession session = _factory.OpenSession())
{
using (SampleContext db = new SampleContext(session))
{
var query = from x in db.Customers
where x.City == city
select x;
return query.ToList();
}
}
}
}
}
دريافت سورس برنامه تا اين قسمت

و در تكميل اين بحث، مي‌توان به ليستي از 101 مثال LINQ ارائه شده در MSDN اشاره كرد كه يكي از بهترين و سريع ترين مراجع يادگيري مبحث LINQ است.


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