Castle ActiveRecord پیاده سازی الگوی ActiveRecord برای پلت فرم دات نت است که در آن هر نمونه از یک کلاس به عنوان یک رکورد در دیتابیس در نظر گرفته می شود که با ساخته شدن یک نمونه از کلاس، یک رکورد در دیتابیس درج می شود و با به روز شدن اطلاعات آن کلاس، رکورد مورد نظر در دیتابیس به روز می شود. متدهای استاتیک درون کلاس ها بر روی کل یا مجموعه ای از رکوردهای دیتابیس عمل می کنند.
Castle ActiveRecord پیاده سازی الگوی ActiveRecord برای پلت فرم دات نت مایکروسافت است که بر روی NHibernate ساخته شده و به عنوان یک لایه دسترسی به داده ها عمل می کند و پیچیدگی های NHibernate را از دید برنامه نویس مخفی نگه می دارد. انجام عمل Mapping در CAR به صورت اضافه کردن خاصیت به کلاس ها انجام می گیرد و شما را از نوشتن فایل های XML برای انجام نگاشت کلاس ها رها می سازد. با استفاده از Castle ActiveRecord دیگر نیازی به یادگیری نحوه نگاشت فایل های XML در NHibernate ندارید و در چند دقیقه می توانید یک لایه دسترسی به داده های کامل و بی نقص را پیاده سازی کنید. اکثر پیچیدگی های NHibernate بوسیله CAR هندل می شوند. در حال حاضر CAR زیر مجموعه ای از قابلیت های نگاشت NHibernate را که توسط اکثر برنامه نویسان و بیشتر سناریوها مورد استفاده قرار می گیرد را پوشش می دهد.
در این مطلب و مطالب آینده، با بررسی یک مثال نحوه استفاده از Castle ActiveRecord به عنوان یک لایه برای دسترسی به داده ها که بر روی NHibernate ساخته شده است را یاد خواهید گرفت. پیش نیاز یادگیری این مطلب آشنایی با مفهوم ORM است. بهتر است قبل از خواندن این نوشته، مطالب زیر را خوانده باشید :
بررسی مدل برنامه مثال
می خواهیم برنامه ای تحت ویندوز برای کتاب فروشی ها بنویسیم که اطلاعات کتاب ها، مشتریان و فروش یک کتاب فروشی کوچک را مدیریت کند. در این برنامه هر کتاب می تواند یک نویسنده و یک ناشر داشته باشد و در دسته بندی های مختلفی قرار بگیرد. کاربر می تواند اطلاعات فروش کتاب ها را به ازای هر مشتری وارد کند. برای درک بهتر مدل برنامه نگاهی به کلاس دیاگرام زیر بیندازید :
برای شروع، آخرین نسخه کتابخانهCastle ActiveRecord را از اینجا دانلود کنید. ویژوال استادیو را باز کنید و یک سولوشن خالی ایجاد کنید. یک پروژه از نوع Class Library با نام MyBookStore.Model به سولوشن خود اضافه کنید و کتابخانه Castle.ActiveRecord را به ارجاعات این پروژه اضافه نمایید. یک کلاس به نام Category به این پروژه اضافه کنید، فضای نام Castle.ActiveRecord را using کنید و کدهای زیر را در بدنه آن بنویسید (کپی کنید!) :
[ActiveRecord(Table="Categories", Lazy=true)]
public class Category: ActiveRecordBase<Category>
{
[PrimaryKey(Generator=PrimaryKeyType.Native)]
public virtual long ID { get; set; }
[Property(NotNull=true, Length=100)]
public virtual string Title { get; set; }
}
اصلاً نگران نباشید اگر منظور Attributeهای اضافه شده به کلاس بالا را متوجه نمی شوید. یکی از مزایای CAR نسبت به استفاده مستقیم از NHibernate همین نحوه نگاشت کلاس ها بر اساس Attributeهاست. در این روش دیگر نیازی به نوشتن فایل های XML برای انجام عمل Mapping ندارید و کدهای نوشته شده در کلاس شما از خوانایی بسیار بالایی برخوردار خواهند بود.
حالا کلاس دیگری به نام Customer به پروژه خود اضافه کنید و کدهای زیر را در آن بنویسید (قبلش فضای نام Castle.ActiveRecord را using کنید ) :
[ActiveRecord(Table="Customers", Lazy=true)]
public class Customer: ActiveRecordBase<Customer>
{
[PrimaryKey(PrimaryKeyType.Native)]
public virtual long ID { get; set; }
[Property(NotNull=true, Length=250)]
public virtual string Title { get; set; }
[Property(Length=50)]
public virtual string ContactNumber { get; set; }
[Property(Length=250)]
public virtual string Email { get; set; }
[HasMany(typeof(Order), Inverse=true, Lazy=true)]
public virtual IList<Order> Orders { get; set; }
}
اگر به کدهای کلاس های بالا دقت کرده باشید نکته هایی را متوجه خواهید شد :
- با اضافه کردن صفت ActiveRecord مشخص می کنیم که این کلاس قرار است به یک Table در دیتابیس Map شود.
- با مقدار دهی Table می توانید نام جدولی که قرار است در دیتابیس ساخته شود را تعیین کنید. اگر مقدار دهی نشود به صورت پیش فرض نام کلاس را برای نام Table انتخاب می کند.
- در هر کلاس یک پراپرتی به نام ID وجود دارد که با صفت PrimaryKey معرفی شده اند. با این کار یک فیلد به نام ID به عنوان کلید اصلی در Table ایجاد می شود و نوع آن Identity خواهد بود (البته در SQL Server)
- اعضایی که با صفت Property مشخص شده اند، فیلدهای Table را مشخص می کنند. هر کدام از این اعضا به صورت پیش فرض Nullable هستند که می توانید با مقدار دهی NotNull برای هر کدام این پیش فرض را تغییر دهید.
- برای اعضای String می توانید Length مشخص کنید.
برای ادامه کلاس های زیر را به پروژه تان اضافه کنید. یادتان نرود در هر کدام فضای نام Castle.ActiveRecord را using کنید!
کلاس Author برای نگهداری اطلاعات یک نویسنده :
Author.cs
[ActiveRecord(Table="Authors", Lazy=true)]
public class Author: ActiveRecordBase<Author>
{
[PrimaryKey(Generator=PrimaryKeyType.Native)]
public virtual long ID { get; set; }
[Property(NotNull=true, Length=100)]
public virtual string Title { get; set; }
کلاس Publisher برای نگهداری اطلاعات ناشر یک کتاب : Publisher.cs
[ActiveRecord(Table="Publishers", Lazy=true)]
public class Publisher: ActiveRecordBase<Publisher>
{
[PrimaryKey(Generator=PrimaryKeyType.Native)]
public virtual long ID { get; set; }</p>
<p dir="ltr">[Property(NotNull=true, Length=100)]
public virtual string Title { get; set; }</p>
<p dir="ltr">[Property(Length=400)]
public virtual string Description { get; set; }
}
کلاس Book برای نگهداری اطلاعات یک کتاب : Book.cs
[ActiveRecord(Table="Books", Lazy=true)]
public class Book: ActiveRecordBase<Book>
{
[PrimaryKey(Generator=PrimaryKeyType.Native)]
public virtual long ID { get; set; }</p>
[Property(NotNull=true, Length=250)]
public virtual string Title { get; set; }
[Property(NotNull=true)]
public virtual decimal BuyPrice { get; set; }
[Property(NotNull=true)]
public virtual decimal SalePrice { get; set; }
[BelongsTo(Column="AuthorID", NotNull=true)]
public virtual Author Author { get; set; }
[BelongsTo(Column="PublisherID", NotNull=true)]
public virtual Publisher Publisher { get; set; }
[HasAndBelongsToMany(typeof(Category), Table="BookCategories" ,
ColumnKey="BookID", ColumnRef="CategoryID")]
public virtual IList Category Categories { get; set; }
}
کلاس های Author و Publisher نکته جدیدی ندارند اما اگر به تعریف کلاس Book توجه کنید Attributeهای جدیدی می بینید. با توجه به مدل برنامه (که تصویر آن در ابتدای مطلب آمده) موجودیت Book با موجودیت های دیگر ارتباط دارد که باید در تعریف این کلاس این ارتباط ها را معرفی کنیم. پراپرتی های Author و Publisher با صفت BelongsTo به همراه NotNull=true مشخص شده اند. این یعنی هر Book باید متعلق به یک Author و یک Publisher مشخص باشد. پراپرتی Categories لیستی از نوع کلاس Category است که در ابتدای مطلب تعریف آن را دیدید. این پراپرتی با صفت HasAndBelongsToMany مشخص شده است که منظور رابطه چند به چند میان کتاب ها و دسته بندی هاست. در این رابطه ColumnKey فیلدی است که آی دی Book و ColumnRef فیلدی است که آی دی یک Category را برای ایجاد رابطه چند به چند ذخیره می کند. در طرف دیگر رابطه (یعنی در کلاس Category) جای ColumnKey و ColumnRef باید عوض شود اما نام Table باید یکی باشد.
تا اینجا رابطه های میان کتاب ها با دیگر موجودیت ها را برای کلاس Book تعریف کردیم. برای اینکه کلاس های دیگر نیز رابطه خودشان با کلاس Book را بدانند باید برای آن ها نوع رابطه را مشخص کنیم. برای این منظور کلاس های فوق را به صورت زیر تغییر دهید:
کلاس Author :
[ActiveRecord(Table="Authors", Lazy=true)]
public class Author: ActiveRecordBase<Author>
{
[PrimaryKey(Generator=PrimaryKeyType.Native)]
public virtual long ID { get; set; }
[Property(NotNull=true, Length=100)]
public virtual string Title { get; set; }
[HasMany(typeof(Book), Inverse=true, Lazy=true)]
public virtual IList<Book> Books { get; set; }
}
کلاس Publisher :
[ActiveRecord(Table="Publishers", Lazy=true)]
public class Publisher: ActiveRecordBase<Publisher>
{
[PrimaryKey(Generator=PrimaryKeyType.Native)]
public virtual long ID { get; set; }
[Property(NotNull=true, Length=100)]
public virtual string Title { get; set; }
[Property(Length=400)]
public virtual string Description { get; set; }
[HasMany(typeof(Book), Inverse=true, Lazy=true)]
public virtual IList<Book> Books { get; set; }
}
کلاس Category :
[ActiveRecord(Table="Categories", Lazy=true)]
public class Category: ActiveRecordBase<Category>
{
[PrimaryKey(Generator=PrimaryKeyType.Native)]
public virtual long ID { get; set; }
[Property(NotNull=true, Length=100)]
public virtual string Title { get; set; }
[HasAndBelongsToMany(typeof(Book), Table="BookCategories", ColumnKey="CategoryID", ColumnRef="BookID")]
public virtual IList<Book> Books { get; set; }
}
همانطور که مشاهده می کنید نیازی نیست تمام تعریف کلاس های بالا را تغییر دهید، فقط کافیست پراپرتی های جدید را به همان کلاس های قبلی اضافه کنید. در کلاس های Author و Publisher یک پراپرتی به نام Books از نوع لیستی از کلاس Book با صفت HasMany تعریف شده است. HasMany مشخص کننده یک رابطه یک به چند از این موجودیت ها به موجودیت «کتاب» است. Inverse=true مشخص می کند که طرف دیگر رابطه یعنی Book مسئول ذخیره کردن اطلاعات است و Lazy=true ویژگی Lazy Loading را برای این مجموعه فعال می کند.
پراپرتی جدید کلاس Category مشخص کننده رابطه چند به چند این موجودیت با موجودیت Book است. اگر به تعریف کلاس Book نگاهی دوباره بیندازید مشاهده می کنید که این رابطه برای کلاس Book تعریف شده است. در Castle ActiveRecord همیشه باید رابطه ها را برای هر دو طرف یک رابطه تعریف کنید. برای یک رابطه چند به چند باید از صفت HasAndBelongsToMany استفاده کنید و ColumnKey، ColumnRef و Table را طوری مقدار دهی کنید که در انتها یک رابطه چند به چند به درستی بین دو موجودیت بوسیله یک Table واسط ایجاد شود. ColumnKey همیشه کلید خارجی موجودیت جاری است و ColumnRef کلید خارجی طرف دیگر رابطه را مشخص می کند. نام Table برای هر دو طرف رابطه باید یکی باشد تا CAR بتواند رابطه چند به چند را برای شما ایجاد کند.
farasun.wordpress.com
این مطلب ادامه دارد…
برای اینکه مطالب بعدی را از دست ندهید، مشترک فید فراسان شوید!
بهاشتراکگذاری این مطلب :
دوستداشتن:
نخستین کسی باشید که این را دوست دارد.
سلام دوست عزیز
من با دات نت برنامه ننوشتم اما ما این کارو تو جاوا با همون )hibernate (annotation انجام میدیم
سلام
من Castle.ActiveRecord را رفرنس کردم ، کلاس Category را هم ایجاد کردم و کدهایی را که گفتید را در آن کپی ! کردم ، اما به ActiveRecord ، ActiveRecordBase و … اررور می ده ! حتی کد را در VB هم نوشتم باز هم همین اتفاق می افتد !
حتی فایل رفرنس را using هم می کنم باز هم فرقی نمی کنه !
ممنون اگه بگید اشکال کار کجاست
@ مجید : سلام، من یادم رفت در مطلب اشاره کنم که باید فضای نام Castle.ActiveRecord را بعد از رفرنس دادن using کنید. اما خب شما این کار را هم کردید و باز هم نمی شناسد. به نظر نمیاد کار شما مشکلی داشته باشه. صبر کنید قسمت دوم هم نوشته بشه و پروژه مثال را دانلود کنید.
خیلی خوبه که یک سری آموزشی راجع به Castle ActiveRecord شروع کردی. چیز به درد بخور و مطمئنی است. موفق باشی.
سلام
آقا ایمان دستت درد نکنه.
واقعا اطلاعات جالبی رو روی سایت قرار میدی.
خیلی جالبن. هم از نظر محتوا و هم از نظر استفاده دوستان .
امیدوارم که همیــــــــــــــــــــــــــــــــــــــــــــــــــــشه سلامت باشی و مارو هم کمک کنی.
بازم تشکر میکنم.
بازتاب: یادگیری Castle ActiveRecord با مثال – قسمت دوم « فراسان
ممنون از مطلب شما.
من تازه کارم توی ORM
تفاوت Fluent و این Castle ActiveRecord چی هستش؟
@ رامین : بله همونطور که خودتون متوجه شدین، Fluent NHibernate تنها کار Mapping را انجام می دهد اما Castle ActiveRecord یک لایه دسترسی به داده های کامل بر روی NHibernate است که بسیاری از پیچیدگی های NH را از دید برنامه نویس مخفی می کند.
آقا ممنون.خودم فهمیدم.
FluentNHibernate and ActiveRecord are quite different. The first one is just codified, convention driven mapping generator for NHibernate. The latter is a layer of abstraction on top of NHibernate along with implementation of the Active Record pattern.
Castle Active Record does hide certain aspects of NHibernate from you, taking care of some things by itself. As such it may be less flexible option if you already have a convoluted existing database.
————–
Where ActiveRecord shines, is when you start your project from scratch, with no legacy DB.
بازتاب: یادگیری Castle ActiveRecord با مثال – قسمت سوم « فراسان
بازتاب: یادگیری Castle ActiveRecord با مثال – قسمت چهارم « فراسان
ممنون از مطالب مفیدت.
من از Castle ActiveRecord بیشتر از Fluent خوشم میاد. اصوالا استفاده از Attribute رو (اگر کارایی اجازه بده) خوب می دونم.
از شما چه پنهان که یک مشاور از خدا بی خبر تو یه پروژه خیلی به ما گیر داد، عشق لینوکس بود و …، خلاصه ما هم گفتیم آقا حالا که اینطور شد با Mono انجام میدیم و ما کلا خیلی خفنیم و این صحبتها
البته دروغ هم نگفتیم، همیشه روی سیستممان لینوکس نصب بود واسه یه همچین روزی.
خلاصه… من با Fluent توی لینوکس و با Mono به راحتی کار کردم. حالا شما که روی ActiveRecord کار کردی میخواستم بدونم ActiveRecord روی لینوکس و Mono چطور کار میکنه؟
حقیقتش به نظر میاد Fluent رسمیتر از ActiveRecord هستش و اعتبار بیشتری داره. نمیدونم…
ممنون.
راستی شما که بر روی وبندوز توسعه میدهی چرا از خود Entity Framework 4.0 استفاده نمیکنی؟
@ امیر : کتابخانه Fluent NHibernate فقط برای انجام عمل Mapping مورد استفاده قرار میگیره تا دیگه نیازی به نوشتن فایل های XML برای نگاشت نداشته باشید اما Castle ActiveRecord یک لایه دسترسی به داده های کامل بر اساس الگوی ActiveRecord بر روی NHibernate است که عملیات نگاشت با استفاده از Attributeها انجام میگیره و بسیاری از پیچیدگی های NHibernate را از دید برنامه نویس پنهان میکنه.
راستش من خودم شخصاً از CAR در مونو استفاده نکردم اما به نظر نمیاد مشکلی باشه.
من از EF هم استفاده میکنم اما دلیلم برای استفاده از NHibernate یکی اوپن سورس بودن و دیگه پشتیبانی از دیتابیس های اوراکل و MySql هست.
ممنون ولی این متنی که در مورد فرق این دو نوشتی رو قبلا هم خوندم و به نظر من فقط و فقط یک تفاوت «قابل توجه» بین این دو هست و اون هم استفاده از Attribute برای نگاشت در CAR هستش.
اشتباه می کنم؟
البته باید متذکر بشم که برای Query گرفتن و اینجور کارها همیشه از Linq استفاده میکنم. چه توی CAR چه NHibernate و بقیه، این خیلی از چیزها رو میپوشونه. به اضافه اینکه اینطور که من بررسی کردم این دو با هم فرق دارند ولی اگر بخواهیم به کارکردشون نگاه کنیم همونطور که گفتم فرق خیلی زیادی ندارند جز در نگاشت به وسیله Attribute.
خلاصه می خوام به این نتیجه برسم که بهتره از Fluent استفاده کنم یا CAR؟
@ امیر : اصلاً درست نیست که بخواهیم Fluent NH را با CAR مقایسه کنیم چون کار این دوتا کاملاً با هم فرق میکنه. اولی فقط برای انجام نگاشت استفاده میشه اما دومی یک لایه دسترسی به داده های کامله.
اگر می خواهید از NHibernaet به صورت مستقیم استفاده کنید بهتره از Fluent NH استفاده کنید اما اگر می خواهید درگیر پیچیدگی های NHibernate نشوید بهتر از CAR استفاده کنید.
من در مورد لایه های زیرین یا جزئیات کارشون بحث نمیکنما! چون همیشه یه لایه برای خودم بالاتر از هر تکنولوژیی که برای دسترسی به داده استفاده کنم میذارم، برای همین هر تکنولوژی که به LINQ2SQL نزدیک باشه و بانکهای اطلاعاتی مختلف رو پشتیبانی کنه برام کافیه. فقط مهم اینه که Performance بالایی داشته باشه و روی Mono اجرا شه. اینطوری هم که من فهمیدم هردوی اینا برای Query گرفتن از Linq پشتیبانی می کنن حالا یهکم توی رسیدن به یک شی IQueryable یا دستورات CUD متفاوت هستند. واسه همینه که میگم این دوتا شبیه هستند.
برای توضیح بیشتر، توی این مقایسهای که من میکنم حتی میشه مثلا کار مستقیم با Query های SQL رو هم شرکت داد (اگر نمیخواستیم Linq رو پشتیبانی کنه).
به هر حال ممنون از وقتی که گذاشتی.
درکل فکر میکنم انتخاب من Fluent باشه.
بازتاب: یادگیری Castle ActiveRecord با مثال – قسمت پنجم « فراسان