مطالب
آشنایی با CLR: قسمت سیزدهم
ترکیب ماژول‌ها به قالب یک اسمبلی
فایل Program.exe یک فایل PE با جداول متادیتا است که همچنین یک اسمبلی هم می‌باشد. یک اسمبلی مجموعه‌ای از یک یا چند فایل، شامل تعاریف نوع و منابع (ریسورس) می‌باشد و یکی از فایل‌های اسمبلی، برای نگهداری manifest انتخاب می‌شود. این جدول مجموعه‌ای است از جداول متادیتا که به طور کلی شامل نام فایل‌هایی است که قسمتی از اسمبلی را تشکیل می‌دهند. برای همین گفتیم که CLR با اسمبلی‌ها کار می‌کند. ابتدا جداول manifest را خوانده تا نام فایل‌ها را شناسایی کرده تا از آن‌ها را به حافظه بارگزاری کند. اسمبلی‌ها چند خصوصیت دارند که باید آن‌ها را بدانید:
- نوع‌های با قابلیت استفاده‌ی مجدد را تعریف می‌کنند.
- داری شماره‌ی نسخه version هستند.
- می‌توانند شامل اطلاعات امنیتی باشند.
این خواصی است که یک اسمبلی به همراه دارد و فایل‌هایی که شامل می‌شود، نمی‌توانند چنین خاصیتی را داشته باشند؛ مگر اینکه آن فایل‌ها در متای خود جدول manifest داشته باشند.
شما برای بسته بندی، شماره نسخه، مباحث امنیتی و استفاده از نوع‌ها، باید آن‌ها را داخل ماژولی قرار دهید که جزئی از اسمبلی است. یک فایل اسمبلی  همانند program.exe به عنوان یک فایل واحد شناخته می‌شود. با اینکه یک اسمبلی از چند فایل تشکیل می‌شود، فایل‌های PE به همراه جداول متادیتای آن و تعدادی ریسورس مثل فایل‌های gif و jpg است که به شما کمک می‌کند به همه‌ی آن‌ها به عنوان یک فایل منطقی EXE یا dll نگاه کنید.
یکی از دلایلی که در قسمت سوم گفتیم این بود که می‌توانیم فایل‌هایی را که به ندرت استفاده می‌شوند، از طریق اینترنت مورد استفاده قرار دهیم. در حالتیکه نیاز به دسترسی به اسمبلی‌های روی اینترنت دارید، CLR ابتدا کش را بررسی می‌کند تا آیا فایل حاضر است یا خیر؟ اگر پاسخ مثبت بود، در حافظه قرار می‌گیرد. ولی اگر پاسخ منفی بود، CLR به آدرسی که اسمبلی در آن قرار دارد، رجوع کرده و آن را دانلود می‌کند و اگر فایل مد نظر یافت نشد، استثنای FileNotFound  را در حین اجرا صادر خواهد کرد.
آقای جفری ریچر در کتاب خود سه تا از دلایل استفاده‌ی از اسمبلی‌های چند فایله را بر می‌شمارد:
  • جداسازی نوع‌ها در فایل‌های جداگانه که باعث کاهش حجم فایل از طریق اینترنت و بارگزاری حجم کمتر در حافظه  می‌شوند.
  • استفاده از فایل‌های منبع و داده‌ها در اسمبلی: فرض کنید نیاز به محاسبه‌ی اطلاعات بیمه دارید و برای این کار به اطلاعات داخل یک جدول آماری احتیاج دارید. این جدول آماری می‌تواند یک فایل متنی ساده یا یک صفحه‌ی گسترده مثل اکسل یا در قالب ورد و هر چیز دیگری باشد که به جای embed شدن این جداول در سورس کد برنامه، آن‌ها را با استفاده از ابزاری مثل Assembly Linker -AL.exe می‌توانید جزئی از اسمبلی کنید و فقط نیاز است که بدانید چگونه آن فایل را پارس یا تبدیل کنید.
  • استفاده از انواع ایجاد شده در زبان‌های مختلف. در این حالت شما مقداری از کد را با استفاده از #C نوشته اید و مقداری از آن را با Visual Basic می‌نویسید و هر کدام در نهایت به یک ماژول جداگانه کامپایل خواهند شد. ولی تبدیل آن به یک واحد منطقی مثل اسمبلی ممکن است و از این نظر می‌توانید روی ماژول‌های یک دسته کنترل داشته باشید.
اگر چندین نوع دارید که شامل نسخه بندی و تنظیمات امنیتی مشترک هستند، بهتر است در یک اسمبلی قرار گیرند تا اینکه در اسمبلی‌های جداگانه‌ایی قرار بگیرند. دلیل این کار هم ایجاد performance یا کارآیی بهتر است. بارگذاری یک  اسمبلی در حافظه زمانی را برای یافتن آن از CLR و ویندوز می‌گیرد و سپس وارد بارگیری آن‌ها در حافظه و آماده سازی می‌شود. پس هر چه تعداد اسمبلی‌ها کمتر باشد، کارآیی بهتری خواهید داشت، چون کمتر شدن بارگیری برابر با کاهش  صفحات کاری است و پراکندگی fragmentation فضای آدرس دهی آن فرایند را کاهش خواهد داد. نهایتا Ngen می‌تواند در بهینه سازی فایل‌های بزرگتر موفق باشد.
برای ساخت اسمبلی، باید یکی از فایل‌های PE را برای نگهداری جدول manifest انتخاب کنید؛ یا خودتان یک فایل PE جدا درست کنید که تنها شامل جدول مانیفست شود. جدول زیر قالبی از جداول مانیفست هست که بابت ماژول‌های اضافه شده به یک اسمبلی ایجاد می‌شوند.
 AssemblyDef  شامل مدخل ورودی (آدرس شروع حافظه) برای اسمبلی‌هایی است که ماژول عضو آن است. این مدخل شامل نام اسمبلی (بدون مسیر و پسوند)، شماره نسخه یا ورژن، culture، فلگ، الگوریتم هش و کلید عمومی ناشر، که می‌تواند نال باشد، هست.
 FileDef  شامل یک مدخل ورودی برای هر فایل PE و فایل‌های ریسورسی است که قسمتی از اسمبلی را تشکیل می‌دهند. این مدخل ورودی شامل نام و پسوند فایل (بدون ذکر مسیر)، فلگ و مقدار هش می‌شود. اگر تنها یک اسمبلی وجود داشته باشد، این جدول هیچ مدخلی نخواهد داشت.
 ManifestResourceDef  شامل یک مدخل ورودی برای هر فایل ریسورس است. این مدخل شامل نام فایل ریسورس، فلگ و یک اندیس به جدول FileDef است که در آن اشاره‌ای به آن فایل ریسورس یا استریم است.
 ExportedTypesDef  شامل یک مدخل ورودی برای هر نوع عمومی است که از همه ماژول‌های PE استخراج شده است. هر مدخل شامل نام نوع و اندیسی به جدول FileDef و یک اندیس دیگر به جدول TypeDef است. نکته: برای ذخیره سازی حافظه و کم حجم شدن فایل‌ها، نوع‌های استخراج شده از فایلی که شامل مانیفست است دیگر در جدول جاری نام نوع‌ها ذکر نمی‌گردد؛ چرا که این اطلاعات در جدول TypeDef اسمبلی جاری موجود است.
  نکته: اسمبلی که شامل مانیفست است، شامل یک جدول AssemblyRef نیز می‌گردد که به تمام اسمبلی‌های ارجاع شده در آن اسمبلی اشاره می‌کند. با استفاده از ابزارهای موجود می‌توان اسمبلی مدنظر را باز کرده و به این ترتیب لیستی از اسمبلی‌های ارجاع شده را خواهید دید و بدین صورت این اسمبلی یک اسمبلی خود تعریف می‌شود.

کامپایلر سی شارپ با استفاده از سوئیچ‌های زیر یک اسمبلی را تولید می‌کند:
/t[arget]:exe, /t[arget]:winexe, /t[arget]: appcontainerexe, /t[arget]: library, or /t[arget]:winmdobj

سوئئیچ‌های بالا باعث می‌شود که یک فایل PE با جدول مانیفست تولید گردد. در صورتیکه سوئیچ زیر را به کار ببرید، فایل تولید شده شامل جدول مانیفست نمی‌شود.
/t[arget]:module
این فایل PE تولید شده در قالب یک dll است که باید قبل از اینکه CLR به نوع‌های داخل آن دسترسی پیدا کند، به یک اسمبلی اضافه گردد. موقعی‌که شما از سوئیچ بالا استفاده می‌کنید، کامپایلر سی شارپ به طور پیش فرض از پسوند netmodule برای فایل خروجی استفاده می‌کند.

نکته‌ی پایانی: محیط توسعه ویژوال استادیو به طور پیش فرض از اسمبلی‌های چند فایل پشتیبانی نمی‌کند، اگر میخواهید که اسمبلی‌های چند فایله تولید کنید باید در سوئیچ‌های مورد استفاده آن تجدید نظری داشته باشید.
در مقاله آینده این روش‌ها را بررسی خواهیم کرد...


نظرات مطالب
Blazor 5x - قسمت 17 - کار با فرم‌ها - بخش 5 - آپلود تصاویر
یک نکته هست که باید توجه کنید برای بهبود عملکرد، شما در همان زمان که فایل انتخاب میشه عمل آپلود رو انجام میدید .که بهتره فایل در یک شی از نوع IBrowseFile بصورت موقت نگهداری بشه و بعد از اینکه فرم بصورت صحیح submit شد و اطلاعات در دیتابیس ثبت شد عمل آپلود فایل انجام بشه.
نکته قابل توجه این هست که امکان داره کاربر بعد از انتخاب فایل عمل submit فرم رو انجام نده ولی فایل به روی سرور آپلود شده باشه،که لازم به این کار نیست.
مطالب
آشنایی با ساختار IIS قسمت اول
در مقاله قبل در مورد نحوه ذخیره سازی در حافظه نوشتیم و به user mode و kernel mode اشاراتی کردیم که می‌توانید به آن رجوع کنید.
در این سری مقالات قصد داریم به بررسی اجزا و روند کاری موجود در IIS بپردازیم که چگونه IIS کار می‌کند و شامل چه بخش هایی می‌شود. مطمئنا آشنایی با این بخش‌ها در روند شناسایی رفتارهای وب اپلیکیشن‌ها و واکنش‌های سرور، کمک زیادی به ما خواهد کرد. در اینجا نسخه IIS7 را به عنوان مرجع در نظر گرفته‌ایم.
وب سرور IIS در عبارت مخفف Internet information services به معنی سرویس‌های اطلاعاتی اینترنت می‌باشد. IIS شامل کامپوننت‌های زیادی است که هر کدام ازآن‌ها کار خاصی را انجام میدهند؛ برای مثال گوش دادن به درخواست‌های ارسال شده به سرور، مدیریت فرآیندها Process و خواندن فایل‌های پیکربندی Configuration؛ این اجزا شامل protocol listener ،Http.sys و WSA و .. می‌شوند.
Protocol Listeners
این پروتکل‌ها به درخواست‌های رسیده گوش کرده و آن‌ها را مورد پردازش قرار می‌دهند و پاسخی را به درخواست کننده، ارسال می‌کنند. هر listener بر اساس نوع پروتکل متفاوت هست. به عنوان مثال کلاینتی، درخواست صفحه‌ای را می‌کند و http listener که به آن Http.sys می‌گویند به آن پاسخ می‌دهد. به طور پیش فرض http.sys به درخواست‌های http و https گوش فرا می‌دهد، این کامپوننت از IIS6 اضافه شده است ولی در نسخه 7 از SSL نیز پشتیبانی می‌کند.
Http.sys یا Hypertext transfer protocol stack
کار این واحد در سه مرحله دریافت درخواست، ارسال آن به واحد پردازش IIS و ارسال پاسخ به کلاینت است؛ قبل از نسخه 6 از Winsock یا windows socket api  که یک کامپوننت user-mod بود استفاده می‌شد ولی Http.sys یک کامپوننت Kernel-mod هست.

Http.sys مزایای زیر را به همراه دارد:

  • صف درخواست مد کرنل: به خاطر اینکه کرنل مستقیما درخواست‌ها را به پروسه‌های مربوطه میفرستد و اگر پروسه موجود نباشد، درخواست را در صف گذاشته تا بعدا پروسه مورد نظر آن را از صف بیرون بکشد.
  • برای درخواست‌ها یک پیش پردازش و همچنین اعمال فیلترهای امنیتی اعمال می‌گردد. 
  • عملیات کش کردن تماما در محیط کرنل مد صورت می‌گیرد؛ بدون اینکه به حالت یوزرمد سوییچ کند. مد کرنل دسترسی بسیار راحت و مستقیمی را برای استفاده از منابع دارد و لازم نیست مانند مد کاربر به لایه‌های زیرین، درخواست کاری را بدهد؛ چرا که خود مستقیما وارد عمل می‌شود و برداشته شدن واسط در سر راه، موجب افزایش عمل caching می‌شود. همچنین دسترسی به کش باعث می‌شود که مستقیما پاسخ از کش به کاربر برسد و توابع پردازشی در حافظه بارگذاری نشوند. البته این کش کردن محدودیت هایی را هم به همراه دارد:
    1. کش کرنل به صورت پیش فرض بر روی صفحات ایستا فعال شده است؛ نه برای صفحاتی با محتوای پویا که البته این مورد قابل تغییر است که نحوه این تغییر را پایینتر توضیح خواهیم داد.
    2. اگر آدرس درخواستی شامل کوئری باشد صفحه کش نخواهد شد:    http://www.site.info/postarchive.htm?id=25 
    3. برای پاسخ ازمکانیزم‌های فشرده سازی پویا استفاده شده باشد مثل gzip کش نخواهد شد
    4. صفحه درخواست شده صفحه اصلی سایت باشد کش نخواهد شد :   http://www.dotnettip.info ولی اگر درخواست بدین صورت باشه http://www.domain.com/default.htm  کش خواهد کرد.
    5. درخواست به صورت ناشناس anonymous نباشد  و نیاز به authentication داشته باشد کش نخواهد شد (یعنی در هدر شامل گزینه authorization می‌باشد).
    6. درخواست باید از نوع نسخه http1 به بعد باشد.
    7. اگر درخواست شامل Entity-body باشد کش نخواهد کرد.
    8. درخواست شامل If-Range/Range header باشد کش نمی‌شود.
    9. کل حجم response بییشتر از اندازه تعیین شده باشد کش نخواهد گردید، این اندازه در کلید ریجستری UriMaxUriBytes قرار دارد. اطلاعات بیشتر
    10. اندازه هدر بیشتر از اندازه تعیین شده باشد که عموما اندازه تعیین شده یک کیلو بایت است.
    11. کش پر باشد، کش انجام نخواهد گرفت.
    برای فعال سازی کش کرنل راهنمای زیر را دنبال کنید:
    گزینه output cache را در IIS، فعال کنید و سپس گزینه Add را بزنید. کادر add cache rule که باز شود، از شما میخواهد یکی از دو نوع کش مد کاربر و مد کرنل را انتخاب کنید و  مشخص کنید چه نوع فایل‌هایی (مثلا aspx) از این قوانین پیروری کنند و مکانیزم کش کردن به سه روش جلوگیری از کش کردن، کش زمان دار و کش بر اساس آخرین تغییر فایل انجام گردد.


    برای تعیین مقدار سایز کش response که در بالا اشاره کردیم می‌توانید در همان پنجره، گزینه edit feature settings را انتخاب کنید.


    این قسمت از مطلب که به نقل از مقاله  آقای Karol Jarkovsky در این آدرس است یک سری تست هایی با نرم افزار(Web Capacity Analysis Tool (WCAT  گرفته است که به نتایج زیر دست پیدا کرده است:
    Kernel Cache Disabled    4 clients/160 threads/30 sec      257 req/sec
    Kernel Cache Enabled     4 clients/160 threads/30 sec      553 req/sec 
    همانطور که می‌بینید نتیجه فعال سازی کش کرنل پاسخ به بیش از دو برابر درخواست در حالت غیرفعال آن است که یک عدد فوق العاده به حساب میاد.
    برای اینکه خودتان هم تست کرده باشید در این آدرس  برنامه را دانلود کنید و به دنبال فایل request.cfg بگردید و از صحت پارامترهای server و url اطمینان پیدا کنید. در گام بعدی 5 پنجره خط فرمان باز کرده و در یکی از آن‌ها دستور netsh http show cachestate را بنویسید تا تمامی وروردی‌های entry که در کش کرنل ذخیره شده اند لیست شوند. البته در اولین تست کش را غیرفعال کنید و به این ترتیب نباید چیزی نمایش داده شود. در همان پنجره فرمان wcctl –a localhost –c config.cfg –s request.cfg  را زده تا کنترلر برنامه در وضعیت listening قرار بگیرد. در 4 پنجره دیگر فرمان wcclient localhost از شاخه کلاینت را نوشته تا تست آغاز شود. بعد از انجام تست به شاخه نصب کنترلر WCAT رفته و فایل log را بخوانید و اگر دوباره دستور نمایش کش کرنل را بزنید باید خالی باشد. حالا کش را فعال کنید و دوباره عملیات تست را از سر بگیرید و اگر دستور netsh را ارسال کنید باید کش کرنل دارای ورودی باشد.
    برای تغییرات در سطح http.sys می‌توانید از ریجستری کمک بگیرید. در اینجا تعداد زیادی از تنظیمات ذخیره شده در ریجستری برای http.sys لیست شده است.
    مطالب
    مفاهیم پایه سیستم های کنترل نسخه؛ قسمت دوم : SVN
    در قسمت قبلی، اهمیت استفاده از سیستم‌های کنترل نسخه را بیان کردیم و مفاهیم پایه‌ای گیت را مورد بررسی قرار دادیم. در این قسمت مفاهیم پایه‌ای SVN را مورد بررسی قرار می‌دهیم.
    SVN مخفف عبارت SubVersion هست و یک سیستم کنترل نسخه‌ی رایگان و متن باز است که توسط شرکت  کلاب نت حمایت می‌شود. به تعدادی از این سیستم‌ها، سیستم‌های «مدیریت پیکربندی نرم افزار»   (Software Configuration Manager (SCM هم اطلاق می‌شود.
    در این سیستم فایل‌ها در یک مخزن Repository مرکزی ذخیره می‌شوند و با هر تغییری که در فایل‌ها و دایرکتوری‌ها ایجاد می‌شود، آن‌ها را ثبت می‌کند. این امکان به ما این اجازه را می‌دهد که نسخه‌ی قدیمی فایل‌ها را بازیابی کرده و تاریخچه‌ی اینکه فایل‌ها چگونه و چه موقع و توسط چه کسی تغییر کرده‌اند، به ما نشان دهد. تصویر سلسله مراتبی زیر به خوبی نحوه ارتباط کلاینت‌ها را به این مخزن نشان می‌دهد.
    SVN برای مدیریت چندین نسخه از فایل ها، از مدل «کپی، ویرایش، ادغام» Copy-Modify-Merge استفاده می‌کند. در این مدل که هر کاربری در مخزن عمل خواندن را انجام می‌دهد، یک کپی جداگانه و کاملا شخصی برای او گرفته شده و سپس کپی‌های شخصی خودش را ویرایش می‌کند. بعد از اینکه ویرایش تکمیل شد، کپی شخصی خودش را با یک فایل جدید و نهایی ادغام می‌کند.
    این روش به شدت از روش «قفل کردن، ویرایش، آزادسازی» »Lock-Modify-Unlock کارآمدتر است و دیگر نیازی نیست که یک کاربر در یک زمان به این ساختار دسترسی داشته باشد و آن را ویرایش کند.

    در تصویر بالا هری و سالی، یک کپی از مخزن موجود را گرفته و سپس هر کدام جداگانه بر روی کپی‌های خودشان مشغول به کار می‌شوند. سپس سالی کارش را زودتر به اتمام رسانده و مخزن را به روز می‌کند. بعد از آن، هری هم کارش به پایان می‌رسد و قصد به روز کردن مخزن را دارد ولی سیستم به او اجازه این کار را نمی‌دهد؛ چون این مخزن آن مخزن نیست که هری قبلا از آن کپی گرفته است. آن مخزن بعد از به روزرسانی سالی تغییر یافته است. پس او مجبور است تا تغییرات جدید مخزن را دریافت کرده و کپی خودش را به روز کند. پس از آن می‌تواند کپی خودش را بر روی مخزن اعمال کند (با فرض اینکه تغییرات جدید هیچ تصادمی با تغییراتی که روی کپی خودش اعمال کرده است ندارند).


    سناریو بالا با احتساب وجود تصادم

    اگر همین سناریوی بالا را فرض کنیم که تغییراتی که هری روی فایل‌ها داده است همان تغییراتی است که سالی قبلا روی مخزن اصلی روی همان فایل‌ها  اعمال کرده است، آیا در این حالت دریافت به روزرسانی‌های جدید باعث ایجاد تصادم می‌شود؟

    هری درخواست ادغام آخرین تغییرات مخزن را با کپی خودش می‌کند. از آنجا که فایل A تصادم دارد یک فلگ flag از این وضعیت می‌گیرد. حال هری میتواند تفاوت‌های ایجاد شده را ببیند و بین آن‌ها یکی را انتخاب کند. در این وضعیت هری همپوشانی‌های کدها را برطرف می‌کند و شاید هم بحثی در مورد این تصادم با سالی داشته باشد تا بهترین تغییر کد انتخاب گردد و نهایتا به روشی کاملا امن و مطمئن، با مخزن اصلی ادغام می‌شود.

    پی نوشت : نرم افزارها نمی‌توانند موضوع تصادم را به طور خودکار اعمال کنند. از آنجا که نیاز به تصمیم گیری و درک هوشمند دارد این کار به صورت انسانی باید بررسی گردد.

    مطالب
    استفاده از DbProviderFactory
    استفاده از DbProviderFactory امکان اتصال به دیتابیس‌های مختلف با یک کد واحد را برای شما فراهم می‌سازد،بطوریکه اگر بخواهید برنامه ای بنویسید که قابلیت اتصال به Oracle و SqlServer و دیگر دیتابیس‌ها را داشته باشد، استفاده از DbProviderFactory ، کار شما را تسهیل می‌نماید.

    DbProviderFactory  در Net Framework 2.0. ارائه شده است.برای درک و چگونگی استفاده از DBProviderFactory مثالی را بررسی می‌نماییم.
    ابتدا کد زیر را درون یک فرم کپی نمایید:
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Data.Common;
    
    namespace DBFactory
    {
        public partial class Form1 : Form
        {
            private string _MySQLProvider = "MySql.Data.MySqlClient";
            private string _SQLProvider="System.Data.SqlClient";
            private string _OracleProvider ="System.Data.OracleClient";
            private DbProviderFactory _DbProviderFactory;
            private DbConnection _DbConnection = null;
            private DbCommand _DbCommand = null;
            private DbDataAdapter _DbDataAdapter = null;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
                try
                {
    
                 string _SQLconnectionstring = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=Test;Data Source=FARHAD-PC";
                 string _Oracleconnectionstring = "Data Source=ServiceName;User Id=Username;Password=Password";
                 
                _DbProviderFactory = DbProviderFactories.GetFactory(_SQLProvider);
                _DbConnection = _DbProviderFactory.CreateConnection();
                _DbConnection.ConnectionString = _SQLconnectionstring;
                
                _DbConnection.Open();
    
                if (_DbConnection.State == ConnectionState.Closed)
                {
    
                    MessageBox.Show("اتصال با دیتابیس برقرار نشده است");
                }
                else
                {
                    MessageBox.Show("اتصال با دیتابیس با موفقیت بر قرار شده است");
                }
                }
                catch (System.Exception excep)
                {
                    MessageBox.Show(excep.Message.ToString());    
                }         
    
            }
        }
    }

    برای استفاد از DBProviderFactory می‌بایست از فضای نامی System.Data.Common استفاده نمایید. بعد از اعلان کلاس فرم تعدادی آبجکت تعریف شده است، که سه آبجکت ابتدایی آن، بیانگر Provider دیتابیس‌های MySQL،SQLSERVER و Oracle می‌باشد:
     private string _MySQLProvider = "MySql.Data.MySqlClient";
     private string _SQLProvider="System.Data.SqlClient";
     private string _OracleProvider ="System.Data.OracleClient";
    Provider‌های بیان شده، جهت استفاده DBFactory برای تشخیص نوع Database می‌باشد، تا بتواند آبجکت‌های مربوط به دیتابیس را ایجاد و در اختیار برنامه نویس قرار دهد. در این مثال ارتباط با دیتابیس SQLSERVER را امتحان می‌کنیم. بنابراین خواهیم داشت:
    _DbProviderFactory = DbProviderFactories.GetFactory("System.Data.SqlClient");

    در کد بالا، Provider، دیتابیس SQLSERVER به DbProviderFactory به عنوان ورودی داده شده است، بنابراین آبجکتهای مربوط به دیتابیس SQL Server ایجاد و در اختیار شما قرار می‌گیرد.

    اگر به نام فضای نامی System.Data.Common توجه نمایید،از کلمه Common استفاده شده است و منظور این است که تمامی کلاسهایی را که این فضای نامی ارائه می‌دهد، در هر دیتابیسی قابل استفاده می‌باشد. برای تشخیص، کلاسهای مربوط به این فضای نامی نیز در ابتدای نام آنها از دو حرف DB استفاده شده است. تمامی کلاسهای زیر در فضای نامی System.Data.Common قابل ارائه و استفاده می‌باشد:
    DbCommand 
    DbCommandBuilder 
    DbConnection 
    DbDataAdapter 
    DbDataReader 
    DbException 
    DbParameter 
    DbTransaction

    جهت اطلاع: ممکن است سئوالی در ذهن شما ایجاد شود که دات نت چگونه براساس نام Provider نوع دیتابیس را تشخیص می‌دهد؟
    جواب: زمانی که دیتابیس‌های مختلف روی سیستم شما نصب می‌شود، Provider‌های مربوط به هر دیتابیس درون فایل Machine.config که مربوط به دات نت میباشد، درج می‌شود. و دات نت براساس اطلاعات مربوط به همین فایل آبجکت‌های دیتابیس را ایجاد می‌نماید.

    امیدوارم مطلب فوق مفید واقع شود.
    مطالب
    تنظیم رشته اتصالی Entity Framework به بانک اطلاعاتی به وسیله کد
    در زمان ساخت مدل از بانک اطلاعاتی در روش Database First به صورت پیش فرض تنظیمات مربوط به اتصال (Connection String) مدل به بانک اطلاعاتی در فایل config برنامه ذخیره می‌شود. مشکل این روش آن است که در سیستم‌های مختلف، بسته به بستری که نرم افزار قرار است بر روی آن اجرا شود، باید تنظیمات مربوط به بانک اطلاعاتی صورت گیرد.
    مثلا فرض کنید شما در زمان توسعه نرم افزار، SQL Server را به صورت Local بر روی سیستم خود نصب کرده اید و Connection String ساخته شده توسط ویزارد Entity Framework بر همین اساس ساخته و ذخیره شده‌است. حال بعد از انتشار برنامه، شخصی تصمیم دارد برنامه را بر روی سیستمی نصب کند که بانک اطلاعاتی Local نداشته و تصمیم به اتصال به یک بانک اطلاعاتی بر روی سرور دیگر یا با مشخصات (Login و Password و ...) دیگر را دارد. برای این مواقع نیاز به پیاده سازی روشی است تا کاربر نهایی بتواند تنظیمات مربوط به اتصال به بانک اطلاعاتی را تغییر دهد.
    روش‌های مختلفی مثل تغییر فایل app.config به صورت Runtime یا ... در سایت‌های مختلف ارائه شده که اکثرا روش‌های غیر اصولی و زمانبری جهت پیاده سازی هستند.
    ساده‌ترین روش جهت انجام این کار، اعمال تغییری کوچک در Constructor کلاس مدل مشتق شده از DBContext می‌باشد. فرض کنید مدلی از بانک اطلاعاتی Personnely با نام PersonallyEntities ساخته اید که حاصل آن کلاس زیر خواهد بود:
        public partial class PersonallyEntities : DbContext
        {
            public PersonallyEntities()
                : base("name=PersonallyEntities")
            {
            }
        }
    همانطور که مشاهده می‌کنید، در Constructor این کلاس، نام Connection String مورد استفاده جهت اتصال به بانک اطلاعاتی به صورت زیر آورده شده که به Connection String ذخیره شده در فایل Config اشاره می‌کند:
    "name=PersonallyEntities"
    اگر به Connection String ذخیره شده در فایل Config دقت کنید متوجه می‌شوید که Connection String ذخیره شده، دارای فرمتی خاص و متفاوتی نسبت به Connection String معمولی ADO.NET است. متن ذخیره شده شامل تنظیمات و Metadata مدل ساخته شده جهت ارتباط با بانک اطلاعاتی نیز می‌باشد:
     metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=Personally;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
    جهت تولید پویای Connection String، بسته به تنظیمات کاربر، نیاز است تا در آخر Connection String ی با فرمت بالا در اختیار Entity Framework قرار دهیم تا امکان اتصال به بانک فراهم شود. جهت تبدیل Connection String معمول ADO.NET به Connection String قابل فهم EF میتوان از کلاس EntityConnectionStringBuilder به صورت زیر استفاده کرد:
            public static string BuildEntityConnection(string connectionString)
            {
                var entityConnection = new EntityConnectionStringBuilder
                {
                    Provider = "System.Data.SqlClient",
                    ProviderConnectionString = connectionString,
                    Metadata = "res://*"
                };
    
                return entityConnection.ToString();
            }
    همانطور که مشاهده می‌کنید، متد بالا با دریافت یک connectionString که همان ADO.NET ConnectionString ما می‌باشد، تنظیمات و Metadata مورد نیاز Entity Framework را به آن اضافه کرده و یک EF ConnectionString برمی‌گرداند.
    برای اینکه بتوان EF ConnectionString تولید شده را در هنگام اجرای برنامه به صورت Runtime اعمال کرد، نیاز است تا تغییر کوچکی در Constructor کلاس مدل تولید شده توسط Entity Framework ایجاد کرد. کلاس PersonnelyEntities به صورت زیر تغییر پیدا می‌کند:

        public partial class PersonallyEntities : DbContext
        {
            public PersonallyEntities(string connectionString)
                : base(connectionString)
            {
    
            }
        }
    با اضافه شدن پارامتر connectionString به سازنده کلاس PersonnelyEntities برای ساخت یک نمونه از مدل ساخته شده در کد نیاز است تا Connection String مورد نظر جهت برقراری ارتباط با بانک را به عنوان پارامتر، به متد سازنده پاس دهیم. سپس مقدار این پارامتر به کلاس والد ( DbContext ) جهت برقراری ارتباط با بانک اطلاعاتی ارجاع داده شده: 
    : base(connectionString)
    در آخر به صورت زیر میتوان توسط EF به بانک اطلاعاتی مورد نظر متصل شد :
    var entityConnectionString = BuildeEntityConnection("Data Source=localhost;Initial Catalog=Personally; Integrated Security=True");
    var PersonallyDb = new PersonallyEntities(entityConnectionString);
    با این روش میتوان ADO Connection String مربوط به اتصال بانک اطلاعاتی را به راحتی به صورت داینامیک به وسیله اطلاعات وارد شده توسط کاربر و کلاس‌های تولید Connection String نظیر SQLConnectionStringBuilder تولید کرد و بدون تغییر در کد‌های برنامه، به بانک‌های مختلفی متصل شد. همچنین با داینامیک کردن متد Provider کلاس EntityConnectionStringBuilder که در کد بالا با "System.Data.SqlClient" مقدار دهی شده، می‌توان وابستگی برنامه بانک اطلاعی خاص را از بین برد و بسته به تنظیمات مورد نظر کاربر، به موتورهای مختلف بانک اطلاعاتی متصل شد که البته لازمه این کار رعایت یکسری نکات فنی در پیاده سازی پروژه است که از حوصله این مقاله خارج است.
    موفق باشید
    مطالب
    با ASP.MVC چه مزایایی را به دست خواهیم آورد
    عموما در اکثر مطالب مقایسه‌ای بین وب فرم‌ها و ASP.NET MVC به جداسازی بهتر منطق کدها از فرم‌ها و قابلیت بهتر تهیه آزمون‌های واحد اشاره می‌شود. در این مطلب از دیدگاهی دیگر به این مساله خواهیم پرداخت؛ از لحاظ فنی و جدای از مسایل یاد شده، چه مزایای دیگری را می‌توان با استفاده از ASP.NET MVC نسبت به وب فرم‌ها به دست آورد؟

    1- آدرس‌های تمیزتر
    در ASP.NET MVC به صورت پیش فرض از سیستم Routing موجود در زیر ساخت ASP.NET برای نمایش Urlهایی بدون پسوند استفاده می‌شود. همچنین این سیستم امکان تهیه آدرس‌هایی با سازگاری بهتر با موتورهای جستجو را نیز از ابتدا مدنظر داشته است.
    بله. این زیر ساخت در اختیار وب فرم‌ها نیز هست؛ اما فرق است بین حالتی که از ابتدا مجبور شویم تمیزتر کار کنیم با زمانیکه این انتخاب را داریم و ... عموما هم از آن در وب فرم‌ها استفاده نمی‌شود.

    2- عدم وابستگی الزامی به فایل‌های فیزیکی موجود در سیستم
    کلیه درخواست‌ها در MVC برخلاف وب فرم‌ها در بدو امر به فایل‌های موجود در سیستم منتقل نمی‌شوند. درخواست‌ها به متدهای موجود در کنترلرها منتقل می‌شوند. همین مساله سبب می‌شود که آدرس‌ها الزاما به یک فایل فیزیکی موجود در سیستم اشاره نکنند. به این ترتیب می‌توان درخواست‌ها را بر اساس شرایط، به Viewهای مختلف هدایت کرد و نه اینکه هر درخواست ابتدا به یک view رسیده و سپس به متدی ارجاع داده شود.
    این مساله از لحاظ امنیتی نیز مهم است. درخواست‌های رسیده به MVC سبب اجرای هیچ فرمی نخواهند شد. درخواست‌ها حتما باید از فیلتر یک کنترلر عبور کنند تا اجرایی شوند.

    3- امکان مدیریت بهتر قسمت‌های مختلف سایت در پوشه‌های جداگانه
    اگر به سورس اکثر سایت‌های مبتنی بر ASP.NET Web forms توجه کنیم، تمام فایل‌های آن‌ها در ریشه سایت قرار دارند منهای فایل‌های CSS و JS و تصاویر. در ASP.NET MVC از ابتدای کار، هر قسمت از سایت در پوشه‌های جداگانه‌ای قرار می‌گیرد و به این ترتیب مدیریت فایل‌ها و نظم دهی به آن‌ها ساده‌تر خواهد بود.

    4- امکان تعریف تمام اجزای یک فرم یا view به صورت strongly typed
    در ASP.NET MVC می‌توان یک کلاس را به یک فرم یا View نسبت داد. به این ترتیب کنترل‌های web forms تبدیل به خواص این کلاس در MVC خواهند شد. مزیت این امر امکان کنترل تمام اجزای فرم‌های سایت توسط کامپایلر است.
    به این ترتیب اگر در طی یک حلقه، جدولی را ایجاد کنیم، تمام عناصر تشکیل دهنده این حلقه (چه کدهای آن و چه المان‌هایی که اطلاعات را در صفحه نمایش می‌دهند) نیز توسط کامپایلر قابل بررسی و خطایابی هستند.

    5- مقدار دهی خودکار مدل متناظر با یک فرم یا View در ASP.NET MVC
    روال متداول کار با وب فرم‌ها، قرار دادن تعدادی کنترل در صفحه و سپس دریافت دستی مقادیر آن‌ها در فایل code behind است. در MVC دیگر نیازی نیست تا این کارها را دستی انجام دهید. به یک فرم یا View کلاسی را انتساب خواهید داد. فریم ورک خواص آن‌را به صورت خودکار در حین ارسال به سرور مقدار دهی خواهد کرد. این مورد حتی در حین کار با jQuery Ajax نیز صادق  است.
    این مساله کار با ORMها را نیز ساده‌تر می‌کند. از این جهت که تمام آن‌ها نهایتا با یک سری مدل و کلاس کار خواهند کرد و تمام فیلدها و جداول به تعدادی کلاس و خاصیت تعریف شده در آن‌ها نگاشت می‌شوند.
    به این ترتیب چون دیگر نیازی به ارجاع مستقیم به اشیاء بصری در فایل‌های code behind که در اینجا کنترلر نام گرفته‌اند نیست، نوشتن آزمون واحد برای این کلاس‌ها نیز به شدت ساده‌تر شده است.

    6- ASP.NET MVC به همراه یک فرم ساز توکار ارائه می‌شود
    اگر کسی به شما گفته است که سرعت کار با ASP.NET MVC پایین است به طور قطع دو فصل اول یک کتاب MVC را بیشتر مطالعه نکرده است. در MVC یک کلاس متناظر با فرمی را طراحی می‌کنید. توسط ابزار scaffolding همراه با VS.NET از روی این کلاس و مدل، با چند کلیک یک فرم تولید خواهد شد. فرمی که حتی مقدار دهی و انتساب عناصر بصری آن به کلاس متناظر با آن نیز خودکار است.
    سرعت پیاده سازی یک برنامه با ASP.NET MVC به مراتب بیشتر است از کار با وب فرم‌ها.

    7- حذف View State در MVC
    از آنجائیکه فرم‌های ASP.NET Web forms از نوع strongly typed نیستند (در دات نت 4 و نیم اندکی بهبود در حد گرید‌های آن حاصل شده البته)، برای اینکه پس از ارسال یک فرم به سرور باز هم کنترل‌های نمایش داده شده در صفحه همان مقادیر قبلی را نمایش دهند، مکانیزم View State به همراه ذخیره سازی اطلاعات فرم در فیلدهای مخفی فرم جاری طراحی شد.
    در MVC نیازی به این مکانیزم نیست. زیرا فقط کافی است که اطلاعات مدل را مجددا به View ارسال کنیم. نمایش و انتساب نهایی آن در اینجا خودکار است. بنابراین نیازی به View State وجود ندارد.

    8- کنترل بهتر بر روی اعتبار سنجی اطلاعات دریافتی
    در وب فرم‌ها اگر بخواهیم سیستم اعتبارسنجی آن‌را غیرفعال کنیم، مثلا برای دریافت html از کاربر، نیاز است کلا آن‌را از کار بیندازیم (یا در سطح فرم یا در سطح کل برنامه). در MVC می‌توان این اعتبار سنجی را تنها در سطح یک خاصیت که قرار است اطلاعات HTML ایی را دریافت کند، غیرفعال کرد؛ نه برای کل فرم و نه در سطح کل برنامه.

    9- امکان استفاده از فرم‌های و Viewهای Razor بجای موتور وب فرم‌ها
    در وب فرم‌ها تا این زمان فقط محدود به تنها موتور نمایشی مخصوص به آن هستیم. اما در MVC این محدودیت برداشته شده و تا به حال چندین و چند View engine در این بین توسط مایکروسافت و سایر برنامه نویس‌ها طراحی شده است. مهم‌ترین آن‌ها Razor است که تمام برنامه نویس‌های MVC پس از مدتی به روان بودن و طراحی طبیعی و عالی آن اعتراف دارند.

    10- امکان تعریف بیش از یک فرم در صفحه
    طراحی ASP.NET Web forms از روز اول آن محدود به یک فرم در صفحه بوده است. این محدودیت در MVC برداشته شده و مزیت آن امکان ارسال اطلاعات قسمت‌های مختلف یک فرم به کنترلرهای مختلف و جداسازی بهتر کدهای قسمت‌های مختلف برنامه است.

    11- امکان Refactoring بهتر کدهای تکراری در ASP.NET MVC به کمک مفهوم فیلترها
    فیلترها در MVC یک سری attribute هستند که می‌توان آن‌ها را به متدهای کنترلرها اعمال کرد و به صورت خودکار توسط فریم ورک پیش یا پس از اجرای یک متد اجرا می‌شوند. به این ترتیب حجم قابل ملاحظه‌ای از if و else‌ها را می‌توان در این فیلترها کپسوله کرد و کدهای متدهای کنترلرها را تمیزتر و زیباتر نمود.

    12- سازگاری کامل با jQuery و jQuery Ajax و کلا انواع و اقسام فریم‌ورک‌های جاوا اسکریپتی
    در MVC وب کنترل‌ها کنار گذاشته شده‌اند و سعی شده است با وب به همان نحو که هست برخورد شود. به این ترتیب اگر نیاز داشتید، کل دکمه‌های فرم را با spanها جایگزین کرده و توسط فریم ورک‌های CSS ایی تزئین کنید، بدون نیاز به نگارش جدیدی از ASP.NET MVC، باز هم برنامه کار خواهد کرد.
    یا برای کار با اجزای مختلف فرم از ده‌ها و صدها افزونه موجود برای jQuery به سادگی می‌توان استفاده کرد. برای نمونه کل سیستم اعتبار سنجی توکار MVC با اعتبار سنجی jQuery یکپارچه و جایگزین شده است.
    یا برای کار با jQuery Ajax نیازی نیست تا متدی را static تعریف کنید و به این ترتیب از مزایای امنیتی توکار ASP.NET محروم شوید (مثلا دسترسی به شیء User اعتبار سنجی مبتنی بر فرم‌ها). یا اگر فرم شما 10 فیلد دارد، کل این فیلدها به صورت خودکار به خواص متناظر با آن‌ها نگاشت خواهد شد و نیازی نیست برای این مورد کد بنویسید. به علاوه باید درنظر داشت که jQuery Ajax نسبت به فریم ورک Ajax همراه با ASP.NET web forms بسیار سبک‌تر و سریعتر عمل می‌کند چون نیازی ندارد تا هر بار View state را نیز به سرور ارسال کند.
    همچنین در اینجا دیگر ID کنترل‌های مورد استفاده در اسکریپت‌ها به صورت خودکار تولید نمی‌شوند و برنامه نویس از ابتدای امر کنترل کاملی را روی این مساله دارد.

    13- امکانات فشرده سازی css و js بهتر
    در MVC 4 سیستم bundling آن از نمونه مشابه موجود در وب فرم‌ها کامل‌تر است و جهت فشرده سازی و یکی کردن هر دو مورد فایل‌های css و js می‌تواند بکارگرفته شود؛ به همراه تنظیمات کش مرورگر و gzip خودکار حاصل. به علاوه این سیستم را سفارشی سازی نیز می‌توان ساخت و بهینه سازی عملکرد آن مطابق نیاز میسر است.

    14- یکپارچگی بهتر EF Code first با MVC
    عنوان شد که وجود مدل‌ها و فرم‌های strongly typed یکی از مزایای کار با MVC است و ORMها نیز نهایتا با همین کلاس‌ها هستند که کار می‌کنند. در MVC سیستم کد سازی به نام scaffolding وجود دارد (تهیه شده توسط خود مایکروسافت) که می‌تواند بر اساس مدل‌های EF code first شما، قسمت عمده‌ای از کدهای یک برنامه ASP.NET MVC را تولید کند. سپس می‌توانید به سفارشی سازی آن مشغول شوید.

    15- تزریق وابستگی‌ها در MVC ساده‌تر است
    در هر دو فریم ورک وب فرم‌ها و MVC امکان تزریق وابستگی‌ها وجود دارد. اما در MVC می‌توان در میانه کار وهله سازی کنترلرها، دخالت کرد و کنترل آن را کاملا در دست گرفت. همین امر سبب می‌شود حین کار با کتابخانه‌های تزریق وابستگی‌ها در ASP.NET MVC حجم کد نویسی به شدت کاهش پیدا کند.

    16- امکانات امنیتی MVC بیشتر است
    عنوان شد که در MVC می‌توان اعتبار سنجی را تنها در حد یک خاصیت غیرفعال کرد. فیلتر مبارزه با حملات CSRF جزئی از فریم ورک MVC است. به همراه فیلتر Authorize آن که باز هم اعمال سفارشی سیستم اعتبار سنجی مبتنی بر فرم‌ها را ساده‌تر می‌کند با امکان یکپارچگی بهتر با Role providerهای سفارشی.
    و یا برای نمونه Razor به صورت پیش فرض امن طراحی شده است. خروجی Razor همواره و در بدو امر، html encoded است مگر اینکه برنامه نویس آگاهانه آن‌را تغییر دهد. این مورد مقاومت در برابر حملات XSS را بالا خواهد برد.
    امکان استفاده از فیلترهای سفارشی که عنوان شد، جهت مسایل امنیتی بسیار کاربرد دارند. برای مثال بررسی referrer فرم ارسال به سرور را درنظر بگیرید. در وب فرم‌ها می‌توان این‌کار را با یک http module که روی کل برنامه تاثیر گذار است انجام داد. اما در MVC این فیلتر را تنها می‌توان بر روی یک فرم خاص عمومی برای مثال اعمال کرد و نه کل برنامه.
    مطالب
    OutputCache در ASP.NET MVC
    مقدمه

    OutputCaching باعث می‌شود خروجیِ یک اکشن متد در حافظه نگهداری شود. با اعمال این نوع کشینگ، ASP.NET در خواست‌های بعدی به این اکشن را تنها با بازگرداندن همان مقدار قبلی ِ نگهداری شده در کش، پاسخ می‌دهد. در حقیقت با OutputCaching از تکرار چند باره کد درون یک اکشن در فراخوانی‌های مختلف جلوگیری کرده‌ایم. کش کردن باعث می‌شود که کارایی و سرعت سایت افزایش یابد؛ اما باید دقت کنیم که چه موقع و چرا از کَش کردن استفاده میکنیم و چه موقع باید از این کار امتناع کرد. 


    فواید کَش کردن

    - انجام عملیات هزینه دار فقط یکبار صورت میگیرد. (هزینه از لحاظ فشار روی حافظه سرور و کاهش سرعت بالا آمدن سایت)

    - بار روی سرور در زمان‌های پیک کاهش می‌یابد.

    - سرعت بالا آمدن سایت بیشتر میشود. 


    چه زمانی باید کَش کرد؟ 

    - وقتی محتوای نمایشی برای همه کاربران یکسان است.

    - وقتی محتوای نمایشی برای نمایش داده شدن، فشار زیادی روی سرور تحمیل میکند.

    - وقتی محتوای نمایشی به شکل مکرر در طول روز باید نمایش داده شود.

    - وقتی محتوای نمایشی به طور مکرر آپدیت نمی‌شود. (در مورد تعریف کیفیت "مکرر"، برنامه نویس بهترین تصمیم گیرنده است) 


    طرح مساله 

    فرض کنید صفحه اول سایت شما دارای بخش‌های زیر است :

    خلاصه اخبار بخش علمی، خلاصه اخبار بخش فرهنگی ، ده کامنت آخر، لیستی از کتگوری‌های موجود در سایت.

    روش‌های مختلفی برای کوئری گرفتن وجود دارد، به عنوان مثال ما به کمک یک یا چند کوئری و توسط یک ViewModel جامع، میخواهیم اطلاعات را به سمت ویو ارسال کنیم. پس در اکشن متد Index ، حجم تقریبا کمی از اطلاعات را باید به کمک کوئری(کوئری های) تقریبا پیچیده ای دریافت کنیم و اینکار به ازای هر ریکوئست هزینه دارد و فشار به سرور وارد خواهد شد. از طرفی میدانیم صفحه اول ممکن است در طول یک یا چند روز تغییر نکند و همچنین شاید در طول یکساعت چند بار تغییر کند! به هر حال در جایی از سایت قرار داریم که کوئری (کوئری های) مورد نظر زیاد صدا زده میشوند ، در حقیقت صفحه اول احتمالا بیشترین فشار ترافیکی را در بین صفحات ما دارد، البته این فقط یک احتمال است و ما دقیقا از این موضوع اطلاع نداریم.

    یکی از راه‌های انجام یک کش موفق و دانستن لزوم کش کردن، این است که دقیقا بدانیم ترافیک سایت روی چه صفحه ای بیشتر است. در واقع باید بدانیم در کدام صفحه "هزینه‌ی اجرای عملیات موجود در کد" بیشترین است.

    فشار ترافیکی(ریکوئست‌های زیاد) و آپدیت‌های روزانه‌ی دیتابیس را، در دو کفه ترازو قرار دهید؛ چه کار باید کرد؟ این تصمیمی است که شما باید بگیرید. نگرانی خود را در زمینه آپدیت‌های روزانه و ساعتی کمتر کنید؛ در ادامه راهی را معرفی میکنیم که آپدیت‌های هر از گاهِ شما، در پاسخِ ریکوئست‌ها دیده شوند. کمی کفه‌ی کش کردن را سنگین کنید.

    به هر حال، فعال کردن قابلیت کش کردن برای یک اکشن، بسیار ساده است، کافیست ویژگی ( attribute ) آن را بالای اکشن بنویسید :

    [OutputCache(Duration = "60", Location = OutputCacheLocation.Server)]
            public ActionResult Index()
            {
                //کوئری یا کوئری‌های لازم برای استفاده در صفحه اصلی و تبدیل آن به یک ویو مدل جامع
            }
    [OutputCache(CacheProfile = "FirstPageIndex",Location=OutputCacheLocation.Server)]
            public ActionResult Index()
            {
                //کوئری یا کوئری‌های لازم برای استفاده در صفحه اصلی و تبدیل آن به یک ویو مدل جامع
            }

    دو روش فوق برای کش کردن خروجی Index  از لحاظ عملکرد یکسان است، به شرطی که در حالت دوم در وب کانفیگ و در بخش system.web آن ، یک پروفایل ایجاد کنیم کنیم :

        <caching>
          <outputCacheSettings>
            <outputCacheProfiles>
              <add name="FirstPageIndex" duration="60"/>
            </outputCacheProfiles>
          </outputCacheSettings>
        </caching>

    در حالت دوم ما یک پروفایل برای کشینگ ساخته ایم و در ویژگی بالای اکشن متد، آن پروفایل را صدا زده ایم. از لحاظ منطقی در حالت دوم، چون امکان استفاده مکرر از یک پروفایل در جاهای مختلف فراهم شده، روش بهتری است. محل ذخیره کش نیز در هر دو حالت سرور تعریف شده است.

    برای تست عملیات کشینگ، کافیست یک BreakPoint  درون Index قرار دهید و برنامه را اجرا کنید. پس از اجرا، برنامه روی Break Point می‌ایستد و اگر F5 را بزنیم، سایت بالا می‌آید. بار دیگر صفحه را رفرش کنیم، اگر این "بار دیگر" در کمتر از 60 ثانیه پس از رفرش قبلی اتفاق افتاده باشد برنامه روی Break Point متوقف نخواهد شد، چون خروجی اکشن، در کش بر روی سرور ذخیره شده است و این یعنی ما فشار کمتری به سرور تحمیل کرده ایم، صفحه با سرعت بالاتری در دسترس خواهد بود.

    ما از تکرار اجرای کد جلوگیری کرده ایم و عدم اجرای کد بهترین نوع بهینه سازی برای یک سایت است. [اسکات الن، پلورال سایت]


    چطور زمان مناسب برای کش کردن یک اکشن را انتخاب کنیم؟

    - کشینگ با زمان کوتاه؛ فرض کنید زمان کش را روی 1 ثانیه تنظیم کرده اید. این یعنی اگر ریکوئست هایی به یک اکشن ارسال شود و همه در طول یک ثانیه اتفاق بیفتد، آن اکشن فقط برای بار اول اجرا میشود، و در بارهای بعد(در طول یک ثانیه) فقط محتوای ذخیره شده در آن یک اجرا، بدون اجرای جدید، نمایش داده میشود. پس سرور شما فقط به یک ریکوئست در ثانیه در طول روز جواب خواهد داد و ریکوئست‌های تقریبا همزمان دیگر، در طول همان ثانیه، از نتایج آن ریکوئست (اگر موجود باشد) استفاده خواهند کرد

    - کشینگ با زمان طولانی؛ ما در حقیقت با اینکار منابع سرور را حفاظت میکنیم، چون عملیاتِ هزینه دار(مثل کوئری‌های حجیم) تنها یکبار در طول زمان کشینگ اجرا خواهند شد. مثلا اگر تنظیم زمان روی عدد 86400 تنظیم شود(یک روز کامل)، پس از اولین ریکوئست به اکشن مورد نظر، تا 24 ساعت بعد، این اکشن اجرا نخواهد شد و فقط خروجی آن نمایش داده خواهد شد. آیا دلیلی دارد که یک کوئری هزینه دار را که قرار نیست خروجی اش در طول روز تغییر کند به ازای هر ریکوئست یک بار اجرا کنیم؟   

    اگر اطلاعات موجود در دیتابیس را تغییر دهیم چه کار کنیم که کشینگ رفرش شود؟ 

    فرض کنید در همان مثال ابتدای این مقاله، شما یک پست به دیتابیس اضافه کرده اید، اما چون مثلا duration مربوط به کشینگ را روی 86400 تعریف کرده اید تا 24 ساعت از زمان ریکوئست اولیه نگذرد، سایت آپدیت نخواهد شد و محتوا همان چیزهای قبلی باقی خواهند ماند. اما چاره چیست؟

    کافیست در بخش ادمین، وقتی که یک پست ایجاد میکنید یا پستی را ویرایش میکند در اکشن‌های مرتبط با Create یا Edit یا Delete چنین کدی را پس از فرمان ذخیره تغییرات در دیتابیس، بنویسید: 

    Response.RemoveOutputCacheItem(Url.Action("index", "home"));

    واضح است که ما داریم کشینگ مرتبط با یک اکشن متد مشخص را پاک میکنیم. با اینکار در اولین ریکوئست پس از تغییرات اعمال شده در دیتابیس، ASP.NET MVC چون میبیند کَشی برای این اکشن وجود ندارد، متد را اجرا میکند و کوئری‌های درونش را خواهد دید و اولین ریکوئست پیش از کَش شدن را انجام خواهد داد. با اینکار کشینگ ریست شده است و پس از این ریکوئست و استخراج اطلاعات جدید، زمان کشینگ صفر شده و آغاز میشود.

    میتوانید یک دکمه در بخش ادمین سایت طراحی کنید که هر موقع دلتان خواست کلیه کش‌ها را به روش فوق پاک کنید! تا اپلیکیشن منتظر ریکوئست‌های جدید بماند و کش‌ها دوباره ایجاد شوند. 


    جمع بندی

    ویژگی OutputCatch دارای پارامترهای زیادیست و در این مقاله فقط به توضیح عملکرد این اتریبیوت اکتفا شده است. بطور کلی این مبحث ظاهر ساده ای دارد، ولی نحوه استفاده از کشینگ کاملا وابسته به هوش برنامه نویس است و پیچیدگی‌های مرتبط با خود را دارد. در واقع خیلی مشکل است که بتوانید یک زمان مناسب برای کش کردن تعیین کنید. باید برنامه خود را در یک محیط شبیه سازی تحت بار قرار دهید و به کمک اندازه گیری و محاسبه به یک قضاوت درست از میزان زمان کش دست پیدا کنید. گاهی متوجه خواهید شد، از مقدار زیادی از حافظه سیستم برای کش کردن استفاده کرده اید و در حقیقت آنقدر ریکوئست ندارید که احتیاج به این هزینه کردن باشد.

    یکی از روش‌های موثر برای دستیابی به زمان بهینه برای کش کردن استفاده از CacheProfile درون وب کانفیگ است. وقتی از کشینگ استفاده میکنید، در همان ابتدا مقدار زمانی مشخص برای آن در نظر نگرفته اید(در حقیقت مقدار زمان مشخصی نمیدانید) پس مجبور به آزمون و خطا و تست و اندازه گیری هستید تا بدانید چه مقدار زمانی را برای چه پروفایلی قرار دهید. مثلا پروفایل هایی به شکل زیر تعریف کرده اید و نام آنها را به اکشن‌های مختلف نسبت داده اید. به راحتی میتوانید از طریق دستکاری وب کانفیگ مقادیر آن را تغییر دهید تا به حالت بهینه برسید، بدون آنکه کد خود را دستکاری کنید. 

    <caching>
          <outputCacheSettings>
            <outputCacheProfiles>
              <add name="Long" duration="86400"/>
              <add name="Average" duration="43600"/>
              <add name="Short" duration="600"/>
            </outputCacheProfiles>
          </outputCacheSettings>
        </caching>

    برای مطالعه جزئیات بیشتر در مورد OutputCaching مقالات زیر منابع مناسبی هستند.

    [اینجا ] و [اینجا ]

    مطالب
    اضافه کردن قابلیت از سرگیری مجدد (resume) به HttpWebRequest

    مطابق RFC مربوطه، اگر هدر درخواست ارسالی به سرور را کمی تغییر دهیم می‌توان بجای شروع از اولین بایت، از بایت مورد نظر شروع به دریافت فایل نمود. (البته این به شرطی است که سرور آن‌را پشتیبانی کند)

    یعنی نیاز داریم که به هدر ارسالی سطر زیر را اضافه کنیم:
    Range: bytes=n-
    که n در اینجا حجم فایل ناقص دریافتی موجود بر حسب بایت است.
    برای بدست آوردن اندازه‌ی فایل ناقص موجود می‌توان از دستور زیر استفاده کرد:
    using System.IO;
    long brokenLen = new FileInfo(fileNamePath).Length;

    سپس اگر شیء webRequest ما به صورت زیر تعریف شده باشد:

    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);

    فقط کافی است سطر زیر را جهت افزودن قابلیت از سرگیری مجدد دریافت فایل به این شیء افزود:

    //دانلود از ادامه
    webRequest.AddRange((int)brokenLen); //resume

    نکته:
    اگر علاقمند باشید که ریز فعالیت‌های انجام شده توسط فضای نام System.Net را ملاحظه کنید، به فایل config خود (مثلا فایل app.config برنامه)، چند سطر زیر را اضافه کنید:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <system.diagnostics>
    <trace autoflush="true" />
    <sources>
    <source name="System.Net">
    <listeners>
    <add name="MyTraceFile"/>
    </listeners>
    </source>
    </sources>

    <sharedListeners>
    <add
    name="MyTraceFile"
    type="System.Diagnostics.TextWriterTraceListener"
    initializeData="System.Net.trace.log"
    />
    </sharedListeners>

    <switches>
    <add name="System.Net" value="Verbose" />
    </switches>

    </system.diagnostics>
    </configuration>
    به این صورت کلیه هدرهای ارسالی به سرور و ریز فعالیت‌های انجام شده در پشت صحنه‌ی کلاس‌های موجود در فایلی به نام System.Net.trace.log برای شما ثبت خواهد شد.


    ملاحظات:
    بدیهی است پیاده سازی قابلیت resume نیاز به موارد زیر خواهد داشت:
    الف) در نظر گرفتن مسیری پیش فرض برای ذخیره سازی فایل‌ها
    ب) پیدا کردن اندازه‌ی فایل موجود بر روی یک سرور و مقایسه‌ی آن با حجم فایل موجود بر روی هارد
    امکان پیدا کردن اندازه‌ی یک فایل هم بدون دریافت کامل آن میسر است. خاصیت ContentLength مربوط به شیء HttpWebResponse بیانگر اندازه‌ی یک فایل بر روی سرور است و صد البته پیش از استفاده از این عدد، مقدار StatusCode شیء نامبرده را بررسی کنید. اگر مساوی OK بود، یعنی این عدد معتبر است.

    مطالب
    مدیریت اطلاعات وابسته به زمان در بانک‌های اطلاعاتی رابطه‌ای
    در یک برنامه فروشگاه، جداول مشتری و خریدهای او را درنظر بگیرید. خرید 3 سال قبل مشتری خاصی به آدرس قبلی او ارسال شده‌است. خرید امروز او به آدرس جدید او ارسال خواهد شد. سؤال: آیا با وارد کردن و به روز رسانی آدرس جدید مشتری، باید سابقه اطلاعاتی قبلی او حذف شود؟ اجناس ارسالی پیشین او، واقعا به آدرس دیگری ارسال شده‌اند و نه به آدرس جدید او. چگونه باید اینگونه اطلاعاتی را که در طول زمان تغییر می‌کنند، در بانک‌های اطلاعاتی رابطه‌ای نرمال شده مدیریت کرد؟ از این نمونه‌ها در دنیای کاری واقعی بسیارند. برای مثال قیمت اجناس نیز چنین وضعی را دارند. یک بستنی مگنوم، سال قبل 300 تومان بود؛ امسال شده است 1500 تومان. یک سطل ماست 2500 تومان بود؛ امروز همان سطل ماست 6500 تومان است. چطور باید سابقه فروش این اجناس را نگهداری کرد؟


    منابع مطالعاتی مرتبط

    این موضوع اینقدر مهم است که تابحال چندین کتاب در مورد آن تالیف شده است:

    Temporal Data & the Relational Model
    Trees and Hierarchies in SQL
    Developing Time-Oriented Database Applications in SQL
    Temporal Data: Time and Relational Databases
    Temporal Database Entries for the Springer Encyclopedia of Database Systems
    Temporal Database Management

    نکته مهمی که در این مآخذ وجود دارند، واژه کلیدی «Temporal data » است که می‌تواند در جستجوهای اینترنتی بسیار مفید واقع شود.


    بررسی ابعاد زمان

    فرض کنید کارمندی را استخدام کرده‌اید که ساعتی 2000 تومان از ابتدای فروردین ماه حقوق دریافت می‌کند. حقوق این شخص از ابتدای مهرماه قرار است به ساعتی 2400 تومان افزایش یابد. اگر مامور مالیات در بهمن ماه در مورد حقوق این شخص سؤال پرسید، ما چه پاسخی را باید ارائه دهیم؟ قطعا در بهمن ماه عنوان می‌کنیم که حقوقش ساعتی 2400 تومان است؛ اما واقعیت این است که این عدد از ابتدای استخدام او ثابت نبوده است و باید تاریخچه تغییرات آن، در نحوه محاسبه مالیات سال جاری لحاظ شود.
    بنابراین در مدل سازی این سیستم به دو زمان نیاز داریم:
    الف) actual time یا زمان رخ دادن واقعه‌ای. برای مثال حقوق شخصی در تاریخ ابتدای مهر ماه تغییر کرده است. به این تاریخ در منابع مختلف Valid time نیز گفته می‌شود.
    ب) record time یا زمان ثبت یک واقعه؛ مثلا زمان پرداخت حقوق. به آن Transaction time هم گفته شده است.
    یک مثال:
     record date  actual date  حقوق دریافتی
    1392/01/01  1392/01/01  2000/روز
    1392/02/01  1392/01/01  2000/روز
    ...
    1392/07/01  1392/07/01  2400/روز
    ...
    1392/17/01  1392/07/01  2400/روز
    در این لیست، ریز حقوق پرداختی به یک شخص را ملاحظه می‌کنید. actual dateها، زمان‌هایی هستند که حقوق پایه شخص در آن‌ها تغییر کرده و record dateها زمان‌هایی هستند که به شخص حقوق داده شده‌است.
    به ترکیب Valid Time  و  Transaction Time، اصطلاحا Bitemporal data می‌گویند.


    مشکلات طراحی‌های متداول اطلاعات وابسته به زمان

    در طراحی‌های متداول، عموما یک جدول کارمندان وجود دارد و یک جدول لیست حقوق‌های پرداختی. رکوردهای لیست حقوق‌های پرداختی نیز توسط یک کلید خارجی به اطلاعات هر کارمند متصل است؛ از این جهت که نمی‌خواهیم اطلاعاتی تکراری را در جدول لیست حقوقی ثبت کنیم و طراحی نرمال سازی شده‌ای مدنظر می‌باشد.
    خوب؛ اول مهرماه حقوق شخصی تغییر کرده است. بنابراین کارمند بخش مالی اطلاعات شخص را به روز می‌کند. با این کار، کل سابقه حقوق‌های پرداختی شخص نیز از بین خواهد رفت. چون وجود این کلید خارجی به معنای استفاده از آخرین اطلاعات به روز شده یک کارمند در جدول لیست حقوقی است. الان اگر از جدول لیست حقوقی گزارش بگیریم، کارمندان همواره از آخرین حقوق به روز شده خودشان استفاده خواهند کرد.


    راه حل‌های متفاوت مدل سازی اطلاعات وابسته به زمان
    برای رفع این مشکل مهم، راه حل‌های متفاوتی وجود دارند که در ادامه آن‌ها را بررسی خواهیم کرد.

    الف) نگهداری اطلاعات وابسته به زمان در جداول نهایی مرتبط
    اگر حقوق پایه شخص در زمان‌های مختلف تغییر می‌کند، بهتر است عدد نهایی این حقوق پرداختی نیز در یک فیلد مشخص، در همان جدول لیست حقوقی ثبت شود. این مورد به معنای داشتن «داده‌ای تکراری» نیست. از این جهت که داده‌ای تکراری است که اطلاعات آن در تمام زمان‌ها، دارای یک مقدار و مفهوم باشد و اطلاعات حقوق یک شخص اینچنین نیست.

    ب) نگهداری اطلاعات تغییرات حقوقی در یک جداول جداگانه
    یک جدول ثانویه حقوق جاری کارمندان مرتبط با جدول اصلی کارمندان باید ایجاد شود. در این جدول هر رکورد آن باید دارای بازه زمانی (valid_start_time و valid_end_time) مشخصی باشد. مثلا از تاریخ X تا تاریخ Y، حقوق کارمند شماره 11 ، 2000 تومان در ساعت بوده است. از تاریخ H تا تاریخ Z اطلاعات دیگری ثبت خواهند شد. به این ترتیب با گزارشگیری از جدول لیست حقو‌ق‌های پرداخت شده، سابقه گذشته اشخاص محو نشده و هر رکورد بر اساس قرارگیری در یک بازه زمانی ثبت شده در جدول ثانویه حقوق جاری کارمندان تفسیر می‌شود.
    در این حالت باید دقت داشت که بازه‌های زمانی تعریف شده، با هم تداخل نکنند و برنامه ثبت کننده اطلاعات باید این مساله را به ازای هر کارمند کنترل کند و یا با ثبت record_date، اجازه ثبت بازه‌های تکراری را نیز بدهد (توضیحات در قسمت بعد).
    به این جدول، یک Temporal table نیز گفته می‌شود. نمونه دیگر آن، نگهداری قیمت یک کالا است از یک تاریخ تا تاریخی مشخص. به این ترتیب می‌توان کوئری گرفت که بستنی مگنوم فروخته شده در ماه آبان سال قبل، بر مبنای قیمت آن زمان، دقیقا چقدر فروش کرده است و نه اینکه صرفا بر اساس آخرین قیمت روز این کالا گزارشگیری کنیم که در این حالت اطلاعات نهایی استخراج شده صحیح نیستند.
    حال اگر به این طراحی در جدولی دیگر Transaction time یا زمان ثبت یک رکورد یا زمان ثبت یک فروش را هم اضافه کنیم، به جداول حاصل Bitemporal Tables می‌گویند.


    مدیریت به روز رسانی‌ها در جداول Temporal
    در جداول Temporal، حذف فیزیکی اطلاعات مطلقا ممنوع است؛ چون سابقه سیستم را تخریب می‌کند. اگر اطلاعاتی در این جداول دیگر معتبر نیست باید تنها تاریخ پایان دوره آن به روز شوند یا یک رکورد جدید بر اساس بازه‌ای جدید ثبت گردد.
    همچنین به روز رسانی‌ها در این جداول نیز معادل هستند با یک Insert جدید به همراه فیلد record_date و نه به روز رسانی واقعی یک رکورد قبلی  (شبیه به سیستم‌های حسابداری باید عمل کرد).
    یک مثال:
    فرض کنید حقوق کارمندی که مثال زده شد، در مهرماه به ساعتی 2400 تومان افزایش یافته است و حقوق نهایی نیز پرداخته شده است. بعد از یک ماه مشخص می‌شود که مدیر عامل سیستم گفته بوده است که ساعتی 2500 تومان و نه ساعتی 2400 تومان! (از این نوع مسایل در دنیای واقعی زیاد رخ می‌دهند!) خوب؛ اکنون چه باید کرد؟ آیا باید رفت و رکورد ساعتی 2400 تومان را به روز کرد؟ خیر. چون سابقه پرداخت واقعی صورت گرفته را تخریب می‌کند. به روز رسانی شما ابدا به این معنا نخواهد بود که دریافتی واقعی شخص در آن تاریخ خاص، ساعتی 2500 بوده است.
    بنابراین در جداول Temporal، تنها «تغییرات افزودنی» مجاز هستند و این تغییرات همواره به عنوان آخرین رکورد جدول ثبت می‌شوند. به این ترتیب می‌توان اصطلاحا «مابه التفاوت» حقوق پرداخت نشده را به شخص خاصی، محاسبه و پرداخت کرد (می‌دانیم در یک بازه زمانی خاص به او چقد حقوق داده‌ایم. همچنین می‌دانیم که این بازه در یک record_date دیگر لغو و با عددی دیگر، جایگزین شده‌است).


    برای مطالعه بیشتر
    Bitemporal Database Table Design - The Basics
    Temporal Data Techniques in SQL
    Database Design: A Point in Time Architecture
    Temporal database
    Temporal Patterns



    راه حلی دیگر؛ استفاده از بانک‌های اطلاعاتی NoSQL
    بانک‌های اطلاعاتی NoSQL برخلاف بانک‌های اطلاعاتی رابطه‌ای برای اعمال Read بهینه سازی می‌شوند و نه برای Write. در چند دهه قبل که بانک‌های اطلاعاتی رابطه‌ای پدیدار شدند، یک سخت دیسک 10 مگابایتی حدود 4000 دلار قیمت داشته است. به همین جهت مباحث نرمال سازی اطلاعات و ذخیره نکردن اطلاعات تکراری تا این حد در این نوع بانک‌های اطلاعاتی مهم بوده است. اما در بانک‌های اطلاعاتی NoSQL امروزی، اگر قرار است فیش حقوقی شخصی ثبت شود، می‌توان کل اطلاعات جاری او را یکجا داخل یک سند ثبت کرد (از اطلاعات شخص در آن تاریخ تا اطلاعات تمام اجزای فیش حقوقی در قالب یک شیء تو در توی JSON). به همین جهت بسیار سریع هستند برای اعمال Read و گزارشگیری. همچنین این نوع سیستم‌ها برای نگهداری نگارش‌های مختلف یک سند بهینه سازی شده‌اند و جزو ساختار توکار آن‌ها است. بنابراین در این نوع سیستم‌ها اگر نیاز است از یک سند خاصی گزارش بگیریم، دقیقا اطلاعات همان تاریخ خاص را دارا است و اگر اطلاعات پایه سیستم را به روز کنیم، از امروز به بعد در سندهای جدید ثبت خواهد شد. این نوع سیستم‌ها رابطه‌ای نیستند و بسیاری از مباحث نرمال سازی اطلاعات در آن‌ها ضرورتی ندارد. قرار است یک فیش حقوقی شخص را نمایش دهیم؟ خوب، چرا تمام اطلاعات مورد نیاز او را در قالب یک شیء JSON تو در توی حاضر و آماده نداشته باشیم؟