‫راه‌هایی برای دینامیک کردن entityهای NHibernate

فرض کنید تعدادی کلاس سی‌شارپ را با استفاده از Reflection.Emit در زمان اجرا (Runtime) ساخته‌اید. این یعنی کلاس‌ها دینامیک بوده و هیچ سورس کدی وجود ندارد، بلکه همه چیز فقط در حافظه وجود دارد یا نهایتاً فایل dll اسمبلی آن را بتوان در دیسک ذخیره کرد. حال می‌خواهیم برای این کلاس‌های دینامیک HBM یا همان فایل mapping مربوط به NHibernate را هم داشته باشیم. هدف نهایی این است که این کلاس‌ها از طریق HBM به یک سری جداول دیتابیسی map شده و بتوان با NHibernate با آن‌ها کار کرد. دقت کنید که نمی‌توان HBMها را از قبل داشت چون خود entityها از قبل وجود ندارند. ما هیچ اطلاعاتی راجع به نام آنها، پراپرتی‌ها، نوع آن‌ها یا دیگر مشخصات آن‌ها نداریم.

من برای انجام این امر یعنی داشتن mapping دینامیک برای entityهای دینامیک زمان Runtime چندین راه را امتحان کردم. با اینکه بیشتر آن‌ها به جواب نرسیدند اما هر کدام نکات جالبی داشتند:

۱- استفاده از فیلدهای XML: اگر تعداد جداول خیلی زیاد شوند، هم نگهداری آن‌ها سخت می‌شود و هم نوشتن queryهای آن خیلی خیلی سخت می‌شود.

۲- تولید کد کلاس سی‌شارپ و HBM با ابزارهایی مثل T4 یا CodeDOM و build آن‌ها و خوراندن آن‌ها به AppDomain جاری: سرعت خیلی پایینی خواهد داشت، احتمالاً دسترسی‌های خاص هم نیاز خواهد داشت. هر تغییری در ساختار entityهای دینامیک موجب restart شدن application خواهد شد. تولید کد کار چندان ساده‌ای نیست.

۳- استفاده از آبجکت دینامیک دات‌نت که از نسخه ۴ اضافه شده است: قابلیت توسعه و دینامیک بودن را در حد fieldها و propertyها می‌دهد. نمی‌توان در زمان اجرا mappingهای جدید اضافه کرد.

۴- استفاده از فیلدهای IDictionary: همان مشکلات راه حل آبجکت دینامیک دات‌نت را دارد.

۵- استفاده از قابلیت جدید mapping by code در NHibernate یا ConfORM یا Fluent NHibernate: به هدف خیلی نزدیک است، چون همه چیز بر اساس type است و هیچ HBMی وجود ندارد. اما تولید delegateها و lambda expressionهای مورد نیاز به صورت runtime اگر کار غیر ممکنی باشد حتماً کار سخت و پیچیده‌ای خواهد بود. در مورد Automapping موجود در FNH کار را خوب راه می‌اندازد اما انعطاف پذیری آن با توجه به محدودیت‌های این کار، آن را غیر قابل استفاده می‌سازد.

۶- استفاده از SetResultTransformer موجود در NHibernate برای query زدن روی جداول دیتابیس بدون نیاز به داشتن Entityها. تا اندازه زیادی جواب می‌دهد! ولی زیادی ساده و بی‌ساختار است. نیاز به نوشتن مقادیر زیادی SQL هم دارد.

۷- کلاس‌های تولیدی توسط Reflection.Emit از همان ابتدا به صورتی باشد که روی تعریف کلاس‌ها و memberها از Attributeهای خاص Castle ActiveRecord استفاده شده باشد. این راه چندان بد به نظر نمی‌رسد اما دو تا مشکل دارد: ۱- استفاده اجباری الگوی Active Record ۲- وابستگی به پروژه Castle ActiveRecord.

۸- ساختن کلاس Mapping از Namespace خود NHibernate: این اولین راهی بود که امتحان کردم. مبنای اولیه کار نمونه کد Ayende Rahien بود. اما متأسفانه به هیچ وجه نتوانستم آن کد را اجرا کنیم. با وجود این گزینه هنوز گزینه خوبی به نظر می‌رسد.

۹- ایده گرفتن از کدهای FNH و ConfORM و Castle ActiveRecord و حتی خود NHibernate: این پروژه در داخل خود کدهایی برای کار با Mappingها دارند. اما حجیم بودن کد آن‌ها مانع از استفاده راحت از آن‌ها می‌شود.

۱۰- استفاده از کتابخانه‌های 3rd party تولید HBM از جداول دیتابیسی: این راه را فقط به عنوان رزرو اینجا آورده‌ام و اصلاً نمی‌دانم که آیا چنین کتابخانه‌هایی وجود دارند یا نه.