۱۳۸۹/۱۱/۲۱

نحوه‌ي نگاشت فيلدهاي فرمول در Fluent NHibernate


اگر با SQL Server كار كرده باشيد حتما با مفهوم و امكان Computed columns (فيلدهاي محاسبه شده) آن آشنايي داريد. چقدر خوب مي‌شد اگر اين امكان براي ساير بانك‌هاي اطلاعاتي كه از تعريف فيلدهاي محاسبه شده پشتيباني نمي‌كنند، نيز مهيا مي‌شد. زيرا يكي از اهداف مهم استفاده‌ي صحيح از ORMs ، مستقل شدن برنامه از نوع بانك اطلاعاتي است. براي مثال امروز مي‌خواهيم با MySQL‌ كار كنيم، ماه بعد شايد بخواهيم يك نسخه‌ي سبك‌تر مخصوص كار با SQLite را ارائه دهيم. آيا بايد قسمت دسترسي به داده برنامه را از نو بازنويسي كرد؟ اينكار در NHibernate فقط با تغيير نحوه‌ي اتصال به بانك اطلاعاتي ميسر است و نه بازنويسي كل برنامه (و صد البته شرط مهم و اصلي آن هم اين است كه از امكانات ذاتي خود NHibernate استفاده كرده باشيد. براي مثال وسوسه‌ي استفاده از رويه‌هاي ذخيره شده را فراموش كرده و به عبارتي ORM مورد استفاده را به امكانات ويژه‌ي يك بانك اطلاعاتي گره نزده باشيد).
خوشبختانه در NHibernate امكان تعريف فيلدهاي محاسباتي با كمك تعريف نگاشت خواص به صورت فرمول مهيا است. براي توضيحات بيشتر لطفا به مثال ذيل دقت بفرمائيد:
در ابتدا كلاس كاربر تعريف مي‌شود:

using System;
using NHibernate.Validator.Constraints;

namespace FormulaTests.Domain
{
public class User
{
public virtual int Id { get; set; }

[NotNull]
public virtual DateTime JoinDate { set; get; }

[NotNullNotEmpty]
[Length(450)]
public virtual string FirstName { get; set; }

[NotNullNotEmpty]
[Length(450)]
public virtual string LastName { get; set; }

[Length(900)]
public virtual string FullName { get; private set; } //از طريق تعريف فرمول مقدار دهي مي‌گردد

public virtual int DayOfWeek { get; private set; }//از طريق تعريف فرمول مقدار دهي مي‌گردد
}
}
در اين كلاس دو خاصيت FullName و DayOfWeek به صورت فقط خواندني به كمك private set ذكر شده، تعريف گرديده‌اند. قصد داريم روي اين دو خاصيت فرمول تعريف كنيم:

using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;

namespace FormulaTests.Domain
{
public class UserCustomMappings : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.Id(u => u.Id).GeneratedBy.Identity(); //ضروري است
mapping.Map(x => x.DayOfWeek).Formula("DATEPART(dw, JoinDate) - 1");
mapping.Map(x => x.FullName).Formula("FirstName + ' ' + LastName");
}
}
}
نحوه‌ي انتساب فرمول‌هاي مبتني بر SQL را در نگاشت فوق ملاحظه مي‌نمائيد. براي مثال FullName از جمع دو فيلد نام و نام خانوادگي حاصل خواهد شد و DayOfWeek از طريق فرمول SQL ديگري كه ملاحظه مي‌نمائيد (يا هر فرمول SQL دلخواه ديگري كه صلاح مي‌دانيد).
اكنون اگر Fluent NHibernate را وادار به توليد اسكريپت متناظر با اين دو كلاس كنيم حاصل به صورت زير خواهد بود:
    create table Users (
UserId INT IDENTITY NOT NULL,
JoinDate DATETIME not null,
FirstName NVARCHAR(450) not null,
LastName NVARCHAR(450) not null,
primary key (UserId)
)
همانطور كه ملاحظه مي‌كنيد در اينجا خبري از دو فيلد محاسباتي تعريف شده نيست. اين فيلدها در تعاريف نگاشت‌ها به صورت خودكار ظاهر مي‌شوند:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" mutable="true"
name="FormulaTests.Domain.User, FormulaTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="Users">
<id name="Id" type="System.Int32" unsaved-value="0">
<column name="UserId" />
<generator class="identity" />
</id>
<property name="DayOfWeek" formula="DATEPART(dw, JoinDate) - 1" type="System.Int32" />
<property name="FullName" formula="FirstName + ' ' + LastName" type="System.String" />
<property name="JoinDate" type="System.DateTime">
<column name="JoinDate" />
</property>
<property name="FirstName" type="System.String">
<column name="FirstName" />
</property>
<property name="LastName" type="System.String">
<column name="LastName" />
</property>
</class>
</hibernate-mapping>
اكنون اگر كوئري زير را در برنامه اجرا نمائيم:
var list = session.Query<User>.ToList();
foreach (var item in list)
{
Console.WriteLine("{0}:{1}", item.FullName, item.DayOfWeek);
}
به صورت خودكار به SQL ذيل ترجمه خواهد شد و اكنون نحوه‌ي بكارگيري فيلدهاي فرمول، بهتر مشخص مي‌گردد:
select
user0_.UserId as UserId0_,
user0_.JoinDate as JoinDate0_,
user0_.FirstName as FirstName0_,
user0_.LastName as LastName0_,
DATEPART(user0_.dw, user0_.JoinDate) - 1 as formula0_, --- همان فرمول تعريف شده است
user0_.FirstName + ' ' + user0_.LastName as formula1_ ---از طريق فرمول تعريف شده حاصل گرديده است
from
Users user0_