‫lazy loading در NHibernate

lazy loading از آن قابلیت‌های جالبی است که همه دوست دارند آن را در برنامه خود داشته باشند. با کمک lazy loading که تقریباً همه ORMها آن را پیاده‌سازی کرده‌اند می‌توان بازیافت اطلاعات از دیتابیس را تا حداکثر زمان ممکن به تعویق انداخت. مثلاً فرض کنید آبجکتی به اسم obj1 دارای یک property به نام obj2 است که obj2 کلاسی است که اطلاعات آن از دیتابیس load می‌شود. در حالت lazy loading وقتی که obj1 از دیتابیس load می‌شود، صرفاً اطلاعات خودش load می‌شود مگر آن که عبارتی به شکل object o1 = obj1.obj2 فراخوانی شود. تازه در این مرحله است که اطلاعات obj2 هم از دیتابیس فراخوانی می‌شود. به این مدل فراخوانی اطلاعات از دیتابیس lazy loading گفته می‌شود چون تا زمانی که واقعاً نیاز به اطلاعات obj2 نباشد، اطلاعات آن از دیتابیس فراخوانی نخواهد شد. lazy یعنی تنبل.

lazy loading در ORMهای مختلف به روش‌های مختلفی انجام می‌شود. مثلاً بعضی‌ها از روش Inheritance استفاده می‌کنند. به این معنی که کلاس Business Entity شما بایستی از یک کلاس مخصوص ارث‌بری کند. این کلاس مخصوص خودش دارای متودهایی برای انجام عملیات lazy loading است. بعضی ORMهای دیگر از روش interface استفاده می‌کنند. در این روش کلاس Business Entity شما باید interface خاصی را implement کند. اما روش NHibernate استفاده از design pattern جالبی به اسم proxy است. در این روش NHiberntae به هنگام شروع به کار و ساختن session، به ازای هر کدام از کلاس‌های Business Entity شما یک کلاس مخصوص می‌سازد و هر جا که برنامه شما نیاز به استفاده از آن کلاس پیدا کرد، NH آن کلاس proxy را تحویلش می‌دهد. این قضیه از دید برنامه مخفی است. یعنی برنامه فکر می‌کند همچنان در حال استفاده از کلاس خودش است. به این ترتیب NH این فرصت را پیدا می‌کند که درخواست‌های دسترسی به propertyهای مختلف یک object را به جای آن که مستقیماً به دیتابیس بفرستد به آن کلاس proxy بفرستد. آن کلاس proxy هم خودش ترتیب lazy loading را می‌دهد. برای آن که یک کلاس proxy بتواند به وظیفه‌اش عمل کند باید کلیه propertyها و methodهای کلاس مبدا به صورت virtual تعریف شده باشد تا proxy factory بتواند همه آنها را در کلاس proxy ارث برده شده، override کند. NH برای تولید proxy از کتابخانه‌های مستقلی مثل Castle Dynamic Proxy استفاده می‌کند.

lazy loading در NH در سه سطح عمل می‌کند: الف- در سطح object. یعنی تا زمانی که واقعاً نیاز به خواندن اطلاعات objectهای دیگری که به صورت property تعریف شده‌اند نباشد، اطلاعات آن از دیتابیس خوانده نخواهد شد. NH فعلا lazy loading در سطح propertyهای غیر آبجکتی را پشتیبانی نمی‌کند.  ۲- در سطح کلی collection: تا زمانی که نیاز به اطلاعات یکی از اعضای آن collection نباشد، اطلات آن از دیتابیس بازیافت نخواهد شد. در این روش همیشه کل collection با هم خوانده می‌شوند. ۳- در سطح اعضای collection: درست مشابه روش قبل است با این تفاوت که lazy loading در سطح تک تک اعضای collection انجام می‌شود. مثلا وقتی که به عنصر چهارم یک collection نیاز هست فقط query همین یک رکورد به دیتابیس ارسال می‌شود نه دیگر اعضای collection. این حالت lazy loading به طور پیش‌فرض به خاطر جلوگیری از کندی سرعت غیر فعال است.

تنها مشکلی که با lazy loading وجود دارد، مشکل آن با session است. در مورد مثال بالا اگر obj1 در حین فعال بودن یک session از دیتابیس فراخوانی شود و آن session بدون فراخوانی obj1.obj2 بسته شود و فراخوانی پس از بسته شدن session انجام شود، خطا رخ می‌دهد چون آن object دیگر detach شده است. غلبه بر این مشکل خیلی سخت نیست. فقط کافی‌ست Close شدن session تا حد امکان به تعویق انداخته شود، مثلا به آخرین خط متود یا try-catch منتقل شود. و اگر این امکان‌پذیر نبود قبل از بسته شدن session، آن property مورد دار به صورت الکی فراخوانی شود تا برنامه یک بار مجبور شود به دیتابیس مراجعه کرده و اطلاعات خود را به روز کند. مشکل sessionها بیشتر در برنامه‌های web مشکل‌ساز هستند چون برنامه‌نویس مجبور است با هر بار Postback برنامه، session را ببندد.