مطالب
مبانی TypeScript؛ تهیه فایل‌های تعاریف نوع‌ها
فایل‌های تعاریف نوع‌ها (Type Definitions) امکان استفاده‌ی ساده‌تر از انواع و اقسام کتابخانه‌های جاوا اسکریپتی موجود را فراهم می‌کنند. این فایل‌ها حاوی تعاریف نوع‌های استفاده شده‌ی در کتابخانه‌های جاوا اسکریپتی هستند که بر اساس TypeScript تهیه نشده‌اند. حاوی هیچ نوع پیاده سازی نیستند و تنها از اینترفیس‌هایی تشکیل می‌شوند که راهنمای کامپایلر TypeScript جهت بررسی نوع‌ها هستند و همچنین به عنوان راهنمای ادیتورهای TypeScript جهت ارائه‌ی Intellisense کاملتر و دقیق‌تری نیز می‌توانند بکار روند. به آن‌ها TypeScript wrapper for JavaScript libraries هم می‌گویند. این فایل‌ها دارای پسوند d.ts. هستند.


منابع یافتن فایل‌های تعاریف نوع‌ها

- بزرگترین مخزن کد فایل‌های تعاریف نوع‌های TypeScript، در سایت Github و در مخزن کد DefinitelyTyped قابل مشاهده است:
https://github.com/DefinitelyTyped/DefinitelyTyped
- همچنین ابزار دیگری به نام «Typings type definition manager» نیز می‌تواند برای این منظور بکار رود.
- علاوه بر این‌ها، بسته‌های npm نیز می‌توانند به همراه تعاریف فایل‌های .d.ts باشند.


مفهوم Ambient Modules

پروژه‌های TypeScript عموما به همراه تعداد زیادی ماژول هستند. به این ترتیب هر ماژول نیاز به d.ts. فایل مخصوص خودش خواهد داشت که نگهداری آن‌ها مشکل خواهد بود. به همین جهت یک Solution متشکل از تعدادی ماژول، می‌تواند تمام تعاریف نوع‌ها را در یک تک فایل d.ts. نگهداری کند که به آن Ambient Module نیز می‌گویند. برای نمونه فایل d.ts. ذیل را درنظر بگیرید:
 // cardCatalog.d.ts
declare module "CardCatalog"{
   export function printCard(callNumber: string): void;
}
در اینجا نحوه‌ی تعریف یک module از نوع ambient را مشاهده می‌کنید که تنها حاوی تعاریف export شده‌است؛ بدون به همراه داشتن پیاده سازی آن‌ها.
سپس برای استفاده‌ی از این فایل d.ts. خواهیم داشت:
 // app.ts
/// <reference path="cardCatalog.d.ts" />
import * as catalog from "CardCatalog";
چون فایل‌های d.ts. دارای پیاده سازی‌های مرتبط نیستند، کار import آن‌ها همانند سایر ماژول‌ها نخواهد بود. ابتدا نیاز است با استفاده از Triple-Slash Directives به ابتدای ماژول فعلی الحاق شوند (مانند مثال فوق). سپس سطر import آن مانند قبل است؛ با این تفاوت که مسیر فایل ماژول را به همراه ندارد و بجای آن نام ماژولی که در فایل d.ts. ذکر شده‌است، تعریف می‌شود.


بررسی مخرن DefinitelyTyped

DefinitelyTyped مخزن کد عظیمی از فایل‌های تعاریف نوع‌های TypeScript است. هرچند دریافت این فایل‌ها از مخزن کد Github آن مانند سایر فایل‌های متداول آن سایت، اما چندین روش دیگر نیز برای کار با این مخزن کد وجود دارد:
- استفاده از NuGet. تقریبا تمام فایل‌های d.ts. آن به صورت یک بسته‌ی نیوگت مجزا نیز وجود دارند.
- استفاده از برنامه‌ی tsd. این برنامه یا type definition manager، به صورت اختصاصی برای کار با این نوع فایل‌ها طراحی شده‌است.
- استفاده از برنامه‌ی typings. این برنامه نیز یک type definition manager دیگر است. مزیت آن کار با چندین منبع مجزای ارائه‌ی فایل‌های d.ts. است که DefinitelyTyped تنها یکی از آن‌ها است.


یک مثال: دریافت مستقیم و افزودن فایل d.ts. مربوط به کتابخانه‌ی جاوا اسکریپتی lodash از مخزن کد DefinitelyTyped

در ادامه قصد داریم فایل تعاریف نوع‌های کتابخانه‌ی معروف lodash را به پروژه‌ی جدیدی در VSCode اضافه کنیم. قدم اول، نصب خود کتابخانه است؛ از این جهت که فایل‌های d.ts.، فاقد هرگونه پیاده سازی هستند.
در مطلب «چرا TypeScript» نحوه‌ی کار با npm را جهت به روز رسانی کامپایلر TypeScript پیش فرض VSCode ملاحظه کردید. در اینجا نیز از npm برای نصب lodash استفاده می‌کنیم:
ابتدا خط فرمان را گشوده و سپس به پوشه‌ی پروژه‌ی خود وارد شوید. سپس دو دستور ذیل را صادر کنید:
npm init -f
npm install lodash --save


در ادامه به مخزن کد DefinitelyTyped وارد شده و پوشه‌ی مربوط به lodash را با جستجو پیدا کنید:
https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/lodash
در این پوشه تنها به فایل lodash.d.ts آن نیاز است. روی لینک این فایل کلیک کرده و سپس در صفحه‌ی باز شده، بر روی دکمه‌ی raw کلیک نمائید. این فایل نهایی را در ریشه‌ی پروژه‌ی جاری ذخیره کنید.
https://github.com/DefinitelyTyped/DefinitelyTyped/raw/master/lodash/lodash.d.ts

اگر به انتهای فایل lodash.d.ts دقت کنید، تعریف ambient module آن چنین شکلی را دارد و export آن lo dash است:
declare module "lodash" {
   export = _;
}
در ادامه برای استفاده‌ی از آن در فایل test.ts، به ابتدای فایل، با استفاده از Triple-Slash Directive، تعریف فایل d.ts. را اضافه کنید:
 /// <reference path="lodash.d.ts" />
سپس جهت دریافت یکجای تمام امکانات این کتابخانه خواهیم داشت:
 import * as _ from "lodash";
و اکنون بلافاصله intellisense به همراه مشخص بودن نوع پارامترهای یک متد فراهم است:
 let snakeCaseTitle = _.snakeCase("test this");
console.log(snakeCaseTitle);


برای گرفتن خروجی از این مثال همانند قبل، ابتدا Ctrl+Shift+P را فشرده و سپس انتخاب tasks:Run build task< و در ادامه فشردن F5 برای اجرا برنامه، نیاز است صورت گیرند:



مدیریت فایل‌های تعاریف نوع‌ها با استفاده از tsd

tsd یک برنامه‌ی خط فرمان است که کار یافتن و دریافت فایل‌های d.ts. را ساده می‌کند. این برنامه منحصرا با مخزن کد DefinitelyTyped کار می‌کند و پس از دریافت هر فایل d.ts.، ارجاعی به آن‌را در فایل tsd.json در ریشه‌ی پروژه ذخیره می‌کند. همچنین یک تک فایل tsd.d.ts حاوی تعاریف Triple-Slash Directive‌ها را نیز تولید می‌کند که در ادامه می‌توان تنها این فایل را به فایل‌های مدنظر الحاق کرد.
البته باید دقت داشت که این برنامه در ابتدای سال 2016 منسوخ شده اعلام گردید و با برنامه‌ی typings جایگزین شده‌است؛ هرچند هنوز هم مفید است و قابل استفاده.
روش دریافت tsd را در سایت definitelytyped.org می‌توانید مشاهده کنید:
http://definitelytyped.org/tsd
نصب آن نیز به صورت یک بسته‌ی npm است:
 npm install tsd -g
توضیحات بیشتر در مورد نحوه‌ی استفاده‌ی از tsd را در مخزن کد آن می‌توانید مشاهده کنید:
https://github.com/Definitelytyped/tsd#readme

برای مثال برای نصب فایل تعاریف نوع‌های lodash، ابتدا به پوشه‌ی پروژه از طریق خط فرمان وارد شده و سپس دستور ذیل را صادر کنید:
 D:\Prog\1395\VSCodeTypeScript>tsd install lodash --save
البته اگر موفق به اجرای این دستور نشدید؛ با خطای ذیل
 [ERR!] Error: connect ECONNREFUSED 10.10.34.36:443
به این معنا است که آدرس فایل‌های raw در github در ایران فیلتر شده‌است و قابل دسترسی نیست (آدرس IP فوق رنج خصوصی است).
اگر موفق به اجرای این دستور شدید، پوشه‌ی جدید typings در ریشه‌ی پروژه ایجاد خواهد شد. داخل آن فایل tsd.d.ts را نیز می‌توان مشاهده کرد که حاوی تعاریف فایل‌های نوع‌های دریافت شده‌است. از این پس در ابتدای فایل‌های ts، بجای تعریف جداگانه‌ی این فایل‌ها، تنها می‌توان نوشت:
 /// <reference path="./typings/tsd.d.ts" />
این تک فایل، reference pathهای تک تک فایل‌های نصب شده‌ی توسط tsd را به همراه دارد.


مدیریت فایل‌های تعاریف نوع‌ها با استفاده از typings

برنامه‌ی typings نیز بسیار شبیه به برنامه‌ی tsd است؛ با این تفاوت که منابع آن منحصر به مخزن کد definitelytyped نیست.
مخزن کد این برنامه در گیت‌هاب قرار دارد: https://github.com/typings/typings
و نصب آن با استفاده از دستور ذیل است:
 npm install typings --global
و اینبار دستور tsd قسمت قبل به نحو ذیل تغییر می‌کند:
 typings install lodash --ambient --save
این مورد نیز قابل استفاده نیست؛ چون به نظر تنها مرجع lodash در حال حاضر github است و آدرس https://raw.githubusercontent.com در ایران فیلتر شده‌است:
 typings ERR! caused by Unable to connect to "https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/299b5caa22876ef27dc8e9a5b7fd7bf93457b6f4/lodash/lodash-3.10.d.ts"
typings ERR! caused by connect ECONNREFUSED 10.10.34.36:443
اگر موفق به نصب این بسته شدید، اکنون پوشه‌ی جدیدی به نام typings در ریشه‌ی سایت ایجاد شده‌است. داخل این پوشه علاوه بر فایل‌های دریافت شده، دو فایل browser.d.ts و main.d.ts را نیز می‌توان مشاهده کرد. فایل browser آن مخصوص برنامه‌های سمت کلاینت است و فایل main آن جهت برنامه‌های NodeJS طراحی شده‌است (که البته در مثال ما هر دو فایل حاوی یک محتوا هستند). این فایل‌ها حاوی تعاریف reference pathهای به فایل‌های نوع‌های نصب شده هستند. بنابراین ابتدای هر فایل ts می‌توان نوشت:
 /// <reference path="./typings/main.d.ts" />
مطالب
آشنایی با Gridify
Gridify چیست ؟

به طور خلاصه Gridify یک کتابخانه ساده و سریع است که عملیات‌های Filtering , Pagination و Sorting را با استفاده از شرط‌های متنی (string based) امکان پذیر میکند.
به طور مثال فرض کنید که یک API را برای دریافت لیست کاربران با نام UsersList نوشته‌اید. مثال:
 [HttpGet("[action]")]
 public async Task<IActionResult> UsersList()
 {
    var users =  await _dbContext.Users.AsNoTracking().ToListAsync();
    return Ok(users);
 }
طبیعتا بخش FrontEnd نرم افزار شما نیاز دارد این اطلاعات را به کاربر نمایش دهد. به همین جهت در بیشتر مواقع از یک گرید برای نمایش این اطلاعات استفاده میشود.
پس از دریافت اطلاعات از سرور با مشکلات زیر مواجه خواهیم شد.
  1. عدم پشتیبانی از Pagination: چون API، تمامی کاربران را به سمت کلاینت ارسال میکند؛ به همین جهت، هم با مشکل کارآیی (performance) در آینده مواجه میشویم و هم امکان گذاشتن صفحه بندی (Pagination) وجود نخواهد داشت. 
  2. عدم پشتیبانی از Sorting: اگر در گرید نمایش داده شده کاربر بخواهد اطلاعات را Sort کند، چون چنین امکانی هنوز برای API ما تعریف نشده، این عملیات سمت سرور امکان پذیر نیست.
  3. عدم پشتیبانی از Filtering: همیشه نمایش تمامی اطلاعات مفید نیست. در اکثر مواقع ما نیاز داریم تا قسمتی از اطلاعات را با شرطی خاص، برگردانیم. به طور مثال لیست کاربران فعال در سامانه یا لیست کاربران غیرفعال. 
این مشکلات بدون استفاده از هیچ کتابخانه‌ای قابل حل است ولی نه به سادگی؛ به طور مثال یا باید چندین API مختلف با امکانات مختلف بنویسیم، یا یک API را برای پشتیبانی از این موارد تغییر بسیار دهیم. من برای اینکه از بحث دور نشویم، به پیاده سازی نمونه دستی پشتیبانی از این موارد در اینجا نمی‌پردازم، چرا که اگر یکبار تلاشی را برای اینکار انجام داده باشیم، طبیعتا  مشکلات و کد کثیفی که در نهایت تولید شده است، یادآوری خواهد شد. 
برای رفع این مشکلات میتوان از کتابخانه Gridify استفاده کرد. مثال :
 [HttpGet("[action]")]
 public async Task<IActionResult> UsersList(GridifyQuery filter)
 {
    var users =  await _dbContext.Users.AsNoTracking().GridifyAsync(filter);
    return Ok(users);
 }
در مثال بالا با استفاده از کلاس GridifyQuery میتوانیم به کنترل هر سه مشکل Sorting - Pagination - Filtering سمت کلاینت بپردازیم. (در ادامه با این کلاس بیشتر آشنا خواهیم شد).


استفاده از Gridify به API‌ها محدود نمیشود. به طور کلی ما میتوانیم در هر نوع لیستی که امکان استفاده از IQueryable  را به ما میدهد از آن استفاده نماییم. 
فرض کنید در یک برنامه Console Application قصد داریم یک فیلتر را از کاربر دریافت کرده و آن را روی لیست خروجی خود اعمال کنیم. به دلیل اینکه امکان جستجوی متنی در دات نت وجود ندارد، برای انجام اینکار مجبور خواهیم شد که برای تک تک فیلدهایی که قرار است برای فیلترینگ پشتیبانی کنیم، یک query جداگانه بنویسیم؛ ولی این عملیات توسط کتابخانه Gridify امکان پذیر است. به طور مثال فرض کنید قصد داریم در لیست کاربران، کاربرانی را  با نام Ali، پیدا کنیم. 
var result = Users.AsQueryable().ApplyFiltering("name==Ali");
این کد دقیقا معادل کد زیر است.
var result = Users.AsQueryable().Where(q => q.Name == "Ali");
در اینجا با استفاده از کتابخانه Gridify ما توانستیم یک static Linq را به یک dynamic Linq که در runtime مقدار دهی خواهد شد، تغییر دهیم. به همین جهت استفاده از مورد اول در برنامه‌ی Console ما امکان پذیر است. تا اینجا ما با امکانات کلی این کتابخانه آشنا شدیم. در مقالات بعدی سعی میکنم به سایر امکانات این کتابخانه و بیشتر به جزئیات بپردازم. همینطور برای کسب اطلاعات بیشتر میتوانید به لینک زیر مراجعه نمایید.
مسیرراه‌ها
سیستم مدیریت محتوای DNTCms
  • قسمت اول - طراحی و پیاده سازی موجودیت‌های وبلاگ
  • قسمت دوم - طراحی و پیاده سازی موجودیت‌های وبلاگ (ادامه) ، پیغام خصوصی و اخبار
  • قسمت سوم - طراحی و پیاده سازی موجودیت‌های انجمن 
  • قسمت چهارم - طراحی و پیاده سازی موجودیت‌های انجمن (ادامه) و سیستم نظرسنجی
  • قسمت پنجم - طراحی و پیاده سازی موجودیت‌های کلکسیون‌ها ، بخش آگهی‌ها ، سیستم لاگ عملیات کاربران و مدل‌های سیستمی
  • قسمت ششم - طراحی و پیاده سازی موجودیت‌های لاگ آماری ، لاگ فعالیت ، سیستم اطلاع رسانی و صفحات داینامیک 
نظرات مطالب
پیاده سازی JSON Web Token با ASP.NET Web API 2.x
در اینجا هش توکن‌ها ذخیره می‌شوند؛ در قسمت «الف) سرویس TokenStoreService » و همچنین در متد TokenEndpointResponse  که دسترسی به آن وجود دارد (توضیحات انتهای قسمت «پیاده سازی قسمت لاگین و صدور access token »). ذخیره سازی هش، هم جستجوی سریعی را به همراه خواهد داشت (چون طول آن کوتاه و ثابت است و امکان تعریف ایندکس بر روی آن وجود دارد. به علاوه موارد منقضی شده هم حذف می‌شوند از بانک اطلاعاتی) و هم نیازی به ذخیره‌ی اطلاعات اضافه‌تری در توکن نهایی ندارد و حجم آن‌را افزایش نمی‌دهد و چون در اختیار کاربر نیست، حتی در صورت افشای کلیدهای سمت سرور، قابل تولید در سمت کلاینت نیست. همچنین بحث تعیین اعتبار توکن‌ها صرفا مرتبط با تائید محل صدور آن‌ها نیست. در سرویس TokenStoreService، وضعیت فعال بودن یا نبودن کاربر، تغییر سطح دسترسی‌ها، تغییر اطلاعات پروفایل او و همچنین عملیات خروج از سیستم او و فاقد اعتبارشدن توکن آن جلسه‌ی کاری نیز بررسی می‌شوند. 
نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 14 - لایه بندی و تزریق وابستگی‌ها
- سطوح بالا و پایین در اینجا بر اساس رابطه‌ی  Presentation Layer-->BLL-->DAL-->Database مشخص می‌شوند (پایین‌ترین سطح بانک اطلاعاتی است و بالاترین سطح، همانی‌است که کاربر با آن تعامل دارد یا لایه‌ی نمایشی). در اینجا DAL بر اساس اینترفیس IUnitOfWork، امکان دسترسی به بانک اطلاعاتی را به صورت کنترل شده، در اختیار BLL (یا همان لایه سرویس در اینجا) قرار می‌دهد (مانند همان IUnitOfWork‌های تزریق شده‌ی در سازنده‌ی کلاس‌های لایه سرویس). سپس BLL هم منطق تجاری تعریف شده‌ی در آن‌را توسط اینترفیس‌هایی در اختیار بالاترین سطح سیستم که لایه نمایش هست (کنترلرها و قسمت MVC)  قرار خواهد داد. پیاده سازی‌های این‌ها هم توسط سیستم تزریق وابستگی‌ها تامین می‌شود و عموما اینترفیس‌های هر لایه هم در همان لایه تعریف می‌شوند و سپس در اختیار لایه‌های بالاتر قرار می‌گیرند. یعنی لایه‌های مختلف از جزئیات پیاده سازی‌های یک‌دیگر مطلع نیستند و فقط با اینترفیس‌ها کار می‌کنند که سهولت نوشتن آزمون‌های واحد را سبب خواهند شد و همچنین امکان تعویض ساده‌تر پیاده سازی‌ها در صورت نیاز، بدون نیاز به تغییرات جزئیات کدنویسی لایه‌ای خاص (اصل باز بودن برای توسعه و بسته بودن برای تغییر).
- همچنین از آنجائیکه ما در یک دنیای محض زندگی نمی‌کنیم، نیاز خواهید داشت از
IUnitOfWork گاهی از اوقات در لایه نمایشی هم استفاده کنید تا بتوانید یک تراکنش حاصل از چند موجودیت و چند کلاس لایه سرویس را در پایان کار درخواست رسیده (فقط یک تراکنش به ازای کل تعاملات یک درخواست)، به بانک اطلاعاتی اعمال کنید. بنابراین internal تعریف کردن آن در دنیای واقعی میسر نیست. حتی برای تعریف سیم‌کشی‌های تزریق وابستگی‌های اولیه‌ی آن هم نباید internal تعریف شود. همچنین باز هم یک برنامه‌ی واقعی الزاما تمام نیازهای تجاری‌اش در لایه سرویس قرار نمی‌گیرد. مثلا نیاز خواهید داشت با میان‌افزارها، اکشن فیلترها و غیره هم کار کنید که این‌ها محل قرارگیری استاندارد و مشخصی ندارند و هر جائی قابل تعریف هستند.
نظرات مطالب
EF Code First #12
سلام آقای نصیری،
شما تو این مقاله گفتید که:
"همچنین نیازی به پیاده سازی متد SaveChanges نیست؛ زیرا پیاده سازی آن در کلاس DbContext قرار دارد."
ولی این متد رو تو اینترفیس IUnitOfWork ذکر کردید،اینجوری که اگه پیاده سازیه این متد رو انجام ندیم ارور میده!

بالاخره این متد باید توی اینترفیس IUnitOfWork ذکر بشه و توی Context هم پیاده سازی بشه
 یا
توی اینترفیس IUnitOfWork ذکر نشه که در اینصورت نیاز باشه توی Context هم پیاده سازی بشه؟
مطالب
آموزش WAF
دز طراحی پروژه‌های مقیاس بزرگ و البته به صورت ماژولار همیشه ساختار پروژه اهمیت به سزایی دارد. متاسفانه این مورد خیلی در طراحی پروژه‌ها در نظر گرفته نمی‌شود و اغلب اوقات شاهد آن هستیم که یک پروژه بسیار بزرگ دقیقا به همان صورت پروژهای کوچک و کم اهمیت‌تر مدیریت و پیاده سازی می‌شود که این مورد هم مربوط به پروژه‌های تحت وب و هم پروژه‌های تحت ویندوز و WPF است. برای مدیریت پروژه‌های WPF و Silverlight در این پست به اختصار درباره PRISM بحث شد. مزایا و معایب آن  بررسی و در طی این پست ها(^ و ^) مثال هایی را پیاده سازی کردیم. اما در این پست مفتخرم شما را با یکی دیگر از کتابخانه‌های مربوط به پیاده سازی مدل MVVM آشنا کنم. کتابخانه ای متن باز، بسیار سبک با کارایی بالا.
اما نکته ای که ذکر آن خالی از لطف نیست این است که قبلا از این کتابخانه در یک پروژه بزرگ  و ماژولار WPF  استفاده کردم و نتیجه مطلوب نیز حاصل شد.
معرفی:
WPF Application Framework یا به اختصار WAF کتابخانه کم حجم سبک و البته با کارایی عالی برای طراحی پروژه‌های ماژولار WPF در مقیاس بزرگ طراحی شده است که مدل پیاده سازی ان بر مبنای مدل MVVM و MVC است. شاید برایتان جالب باشد که این کتابخانه دقیقا مدل MVC را با مدل MVVM ترکیب کرده در نتیجه مفاهیم آن بسیار شبیه به پروژه‌های تحت وب MVC است. همانطور که از نام آن پیداست این کتابخانه صرفا برای پروژه‌های WPF طراحی شده، در نتیجه در پروژه‌های Silverlight نمی‌توان از آن استفاده کرد.
ساختار کلی آن به شکل زیر می‌باشد:

همانطور که مشاهده می‌کنید پروژه‌های مبتنی بر این کتابخانه همانند سایر کتابخانه‌های MVVM از سه بخش تشکیل شده اند. بخش اول با عنوان Shell یا Presentation معرف فایل‌های Xaml پروژه است، بخش دوم یا Application معرف ViewModel و Controller و البته IView  می‌باشد. بخش Domain نیز در برگیرنده مدل‌های برنامه است.

معرفی برخی مفاهیم:

»Shell :  این کلاس معادل یک فایل Xaml است که حتما باید یک اینترفیس IView را پیاده سازی نماید.
»IView : معرف یک اینترفیس جهت برقراری ارتباط بین ViewModel و Shell
»ViewModel : در این جا ViewModel با مفهوم ViewModel در سایر کتابخانه‌های MVVM  کمی متفاوت است. در این کتابخانه ViewModel فقط شامل تعاریف است  و هیچ گونه پیاده سازی در اینجا صورت نمی‌گیرد. دقیقا معادل مفهوم ViewModel در پروژه‌های MVC تحت وب.
»Controller : پیاده سازی ViewModel و تعریف رفتارها در این قسمت انجام می‌گیرد.

اما در بسیاری از پروژها نیاز به پیاده سازی الگوی DataModel-View-ViewModel است که این کتابخانه با دراختیار داشتن برخی کلاس‌های پایه این مهم را برایمان میسر کرده است.

همانطور که می‌بینید در این حالت بر خلاف حالت قبلی ViewModel  و کنترلر‌های پروژه به جای ارتباط با مدل با مفهوم DataModel تغذیه می‌شوند که یک پیاده سازی سفارشی از مدل‌های پروژه است. هم چنین این کتابخانه یک سری Converter‌های سفارشی جهت تبدیل Model به DataModel و برعکس را ارائه می‌دهد.
سرویس‌های پیش فرض: که شامل DialogBox جهت نمایش پیغام‌ها و Save|Open File Dialog سفارشی نیز می‌باشد.
 »برای پیاده سازی Modularity از کتابخانه MEF استفاده شده است.
Command‌های سفارشی: پیاده سازی خاص از اینترفیس ICommand
»مفاهیم مربوط به Weak Event Pattern به صورت توکار در این کتابخانه تعبیه شده است.
»به صورت پیش فرض مباحث مربوط به اعتبارسنجی با استفاده از DataAnnotation   و IDataErrorInfo در این کتابخانه تعبیه شده است.
»ارائه Extension‌های مربوط به UnitTest نظیر Exceptions  و CanExecuteChangedEvent و PopertyChanged جهت سهولت در تهیه unit test

دانلود و نصب
با استفاده از nuget  و دستور زیر می‌توانید این کتابخانه را نصب نمایید:
Install-Package waf
هم چنین می‌توانید سورس آن به همراه فایل‌های باینری را از اینجا دریافت کنید. در پست بعدی یک نمونه از پیاده سازی مثال با این کتابخانه را بررسی خواهیم کرد.
مطالب دوره‌ها
استفاده از async و await در برنامه‌های ASP.NET Web forms 4.5
سؤال: چه زمانی از متدهای async و چه زمانی از متدهای همزمان بهتر است استفاده شود؟

از متدهای همزمان متداول برای انجام امور ذیل استفاده نمائید:
- جهت پردازش اعمالی ساده و سریع
- اعمال مدنظر بیشتر قرار است بر روی CPU اجرا شوند و از مرزهای IO سیستم عبور نمی‌کنند.

و از متدهای غیرهمزمان برای پردازش موارد زیر کمک بگیرید:
- از وب سرویس‌هایی استفاده می‌کنید که متدهای نگارش async را نیز ارائه داده‌اند.
- عمل مدنظر network-bound و یا I/O-bound است بجای CPU-bound. یعنی از مرزهای IO سیستم عبور می‌کند.
- نیاز است چندین عملیات را به موازات هم اجرا کرد.
- نیاز است مکانیزمی را جهت لغو یک عملیات طولانی ارائه دهید.


مزایای استفاده از متدهای async در ASP.NET

استفاده از await در ASP.NET، ساختار ذاتی پروتکل HTTP را که اساسا یک synchronous protocol، تغییر نمی‌دهد. کلاینت، درخواستی را ارسال می‌کند و باید تا زمان آماده شدن نتیجه و بازگشت آن از طرف سرور، صبر کند. نحوه‌ی تهیه‌ی این نتیجه، خواه async باشد و یا حتی همزمان، از دید مصرف کننده کاملا مخفی است. اکنون سؤال اینجا است که چرا باید از متدهای async استفاده کرد؟
- پردازش موازی: می‌توان چند Task را مثلا توسط Task.WhenAll به صورت موازی با هم پردازش کرده و در نهایت نتیجه را سریعتر به مصرف کننده بازگشت داد. اما باید دقت داشت که این Taskها اگر I/O bound باشند، ارزش پردازش موازی را دارند و اگر compute bound باشند (اعمال محاسباتی)، صرفا یک سری ترد را ایجاد و مصرف کرده‌اید که می‌توانسته‌اند به سایر درخواست‌های رسیده پاسخ دهند.
- خالی کردن تردهای در حال انتظار: در اعمالی که disk I/O و یا network I/O دارند، پردازش موازی و اعمال async به شدت مقیاس پذیری سیستم را بالا می‌برند. به این ترتیب worker thread جاری (که تعداد آن‌ها محدود است)، سریعتر آزاد شده و به worker pool بازگشت داده می‌شود تا بتواند به یک درخواست دیگر رسیده سرویس دهد. در این حالت می‌توان با منابع کمتری، درخواست‌های بیشتری را پردازش کرد.


ایجاد Asynchronous HTTP Handlers در ASP.Net 4.5

در نگارش‌های پیش از دات نت 4.5، برای نوشتن فایل‌های ashx غیرهمزمان می‌بایستی اینترفیس IHttpAsynchHandler پیاده سازی می‌شد که نحوه‌ی کار با آن از مدل APM پیروی می‌کرد؛ نیاز به استفاده از یک سری callback داشت و این عملیات باید طی دو متد پردازش می‌شد. اما در دات نت 4.5 و با معرفی امکانات async و await، نگارش سازگاری با پیاده سازی کلاس پایه HttpTaskAsyncHandler فراهم شده است.
برای آزمایش آن، یک برنامه‌ی جدید ASP.NET Web forms نگارش 4.5 یا بالاتر را ایجاد کنید. سپس از منوی پروژه، گزینه‌ی Add new item یک Generic handler به نام LogRequestHandler.ashx را به پروژه اضافه نمائید.
زمانیکه این فایل به پروژه اضافه می‌شود، یک چنین امضایی را دارد:
 public class LogRequestHandler : IHttpHandler
IHttpHandler آن‌را اکنون به HttpTaskAsyncHandler تغییر دهید. سپس پیاده سازی ابتدایی آن به شکل زیر خواهد بود:
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace Async14
{
    public class LogRequestHandler : HttpTaskAsyncHandler
    {
        public override async Task ProcessRequestAsync(HttpContext context)
        {
            string url = context.Request.QueryString["rssfeedURL"];
            if (string.IsNullOrWhiteSpace(url))
            {
                context.Response.Write("Rss feed URL is not provided");
            }

            using (var webClient = new WebClient {Encoding = Encoding.UTF8})
            {
                webClient.Headers.Add("User-Agent", "LogRequestHandler 1.0");
                var rssfeed = await webClient.DownloadStringTaskAsync(url);
                context.Response.Write(rssfeed);
            }
        }

        public override bool IsReusable
        {
            get { return true; }
        }

        public override void ProcessRequest(HttpContext context)
        {
            throw new Exception("The ProcessRequest method has no implementation.");
        }
    }
}
واژه‌ی کلیدی async را نیز جهت استفاده از await به نسخه‌ی غیرهمزمان آن اضافه کرده‌ایم.
در این مثال آدرس یک فید RSS از طریق کوئری استرینگ rssfeedURL دریافت شده و سپس محتوای آن به کمک متد DownloadStringTaskAsync دریافت و بازگشت داده می‌شود.
برای آزمایش آن، مسیر ذیل را درخواست دهید:
 http://localhost:4207/LogRequestHandler.ashx?rssfeedURL=https://www.dntips.ir/feed/latestchanges
کاربردهای فایل‌های ashx برای مثال ارائه فید‌های XML ایی یک سایت، ارائه منبع نمایش تصاویر پویا از بانک اطلاعاتی، ارائه JSON برای افزونه‌های auto complete جی‌کوئری و امثال آن است. مزیت آن‌ها سربار بسیار کم است؛ زیرا وارد چرخه‌ی طول عمر یک صفحه‌ی aspx معمولی نمی‌شوند.


صفحات async در ASP.NET 4.5

در قسمت‌های قبل مشاهده کردیم که در برنامه‌های دسکتاپ، به سادگی می‌توان امضای روال‌های رخداد گردان را به async تغییر داد و ... برنامه کار می‌کند. به علاوه از مزیت استفاده از واژه کلیدی await نیز در آن‌ها برخوردار خواهیم شد. اما ... هرچند این روش در وب فرم‌ها نیز صادق است (مثلا public void Page_Load را به  public async void Page_Load می‌توان تبدیل کرد) اما اعضای تیم ASP.NET آن‌را در مورد برنامه‌های وب فرم توصیه نمی‌کنند:
Async void event handlers تنها در مورد تعداد کمی از روال‌های رخدادگردان ASP.NET Web forms کار می‌کنند و از آن‌ها تنها برای تدارک پردازش‌های ساده می‌توان استفاده کرد. اگر کار در حال انجام اندکی پیچیدگی دارد، «باید» از PageAsyncTask استفاده نمائید. علت اینجا است که Async void یعنی fire and forget (کاری را شروع کرده و فراموشش کنید). این روش در برنامه‌های دسکتاپ کار می‌کند، زیرا این برنامه‌ها مدل طول عمر متفاوتی داشته و تا زمانیکه برنامه از طرف OS خاتمه نیابد، مشکلی نخواهند داشت. اما برنامه‌های بدون حالت وب متفاوتند. اگر عملیات async پس از خاتمه‌ی طول عمر صفحه پایان یابد، دیگر نمی‌توان اطلاعات صحیحی را به کاربر ارائه داد. بنابراین تا حد ممکن از تعاریف async void در برنامه‌های وب خودداری کنید.

تبدیل روال‌های رخدادگردان متداول وب فرم‌ها به نسخه‌ی async شامل دو مرحله است:
الف) از متد جدید RegisterAsyncTask که در کلاس پایه Page قرار دارد برای تعریف یک PageAsyncTask استفاده کنید:
using System;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;

namespace Async14
{
    public partial class _default : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            RegisterAsyncTask(new PageAsyncTask(LoadSomeData));
        }

        public async Task LoadSomeData()
        {
            using (var webClient = new WebClient { Encoding = Encoding.UTF8 })
            {
                webClient.Headers.Add("User-Agent", "LogRequest 1.0");
                var rssfeed = await webClient.DownloadStringTaskAsync("url");

                //listcontacts.DataSource = rssfeed;
            }
        }
    }
}
با استفاده از System.Web.UI.PageAsyncTask می‌توان یک async Task را در روال‌های رخدادگردان ASP.NET مورد استفاده قرار داد.

ب) سپس در کدهای فایل aspx، نیاز است خاصیت async را نیز true نمائید:
 <%@ Page Language="C#" AutoEventWireup="true"
Async="true"
  CodeBehind="default.aspx.cs" Inherits="Async14._default" %>


تغییر تنظیمات IIS برای بهره بردن از پردازش‌های Async

اگر از ویندوزهای 7، ویستا و یا 8 استفاده می‌کنید، IIS آن‌ها به صورت پیش فرض به 10 درخواست همزمان محدود است.
بنابراین تنظیمات ذیل مرتبط است به یک ویندوز سرور و نه یک work station :
به IIS manager مراجعه کنید. سپس برگه‌ی Application Pools آن‌را باز کرده و بر روی Application pool برنامه خود کلیک راست نمائید. در اینجا گزینه‌ی Advanced Settings را انتخاب کنید. در آن Queue Length را به مثلا عدد 5000 تغییر دهید. همچنین در دات نت 4.5 عدد 5000 برای MaxConcurrentRequestsPerCPU نیز مناسب است. به علاوه عدد connectionManagement/maxconnection را نیز به 12 برابر تعداد هسته‌های موجود تغییر دهید.
مطالب
Globalization در ASP.NET MVC - قسمت ششم
در قسمت قبل ساختار اصلی و پیاده‌سازی ابتدایی یک پرووایدر سفارشی دیتابیسی شرح داده شد. در این قسمت ادامه بحث و مطالب پیشرفته‌تر آورده شده است.

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


نام این جدول را با درنظر گرفتن شرایط موجود می‌توان Resources گذاشت.

ستون Name برای ذخیره نام منبع درنظر گرفته شده است. این نام برابر نام منابع درخواستی در سیستم مدیریت منابع ASP.NET است که درواقع برابر همان نام فایل منبع اما بدون پسوند resx. است.

ستون Key برای نگهداری کلید ورودی منبع استفاده می‌شود که دقیقا برابر همان مقداری است که درون فایلهای resx. ذخیره می‌شود. 

ستون Culture برای ذخیره کالچر ورودی منبع به کار می‌رود. این مقدار می‌تواند برای کالچر پیش‌فرض برنامه برابر رشته خالی باشد. 

ستون Value نیز برای نگهداری مقدار ورودی منبع استفاده می‌شود. 

برای ستون Id می‌توان از GUID نیز استفاده کرد. در اینجا برای راحتی کار از نوع داده bigint و خاصیت Identity برای تولید خودکار آن در Sql Server استفاده شده است.

نکته: برای امنیت بیشتر می‌توان یک Unique Constraint بر روی سه فیلد Name و Key و Culture اعمال کرد.

برای نمونه به تصویر زیر که ذخیره تعدای ورودی منبع را درون جدول Resources نمایش می‌دهد دقت کنید:

 

اصلاح کلاس DbResourceProviderFactory

برای ذخیره منابع محلی، جهت اطمینان از یکسان بودن نام منبع، متد مربوطه در کلاس DbResourceProviderFactory باید به‌صورت زیر تغییر کند:

public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
{
  if (!string.IsNullOrEmpty(virtualPath))
  {
    virtualPath = virtualPath.Remove(0, virtualPath.IndexOf('/') + 1); // removes everything from start to the first '/'
  }
  return new LocalDbResourceProvider(virtualPath);
}
با این تغییر مسیرهای درخواستی چون "Default.aspx/~" و یا "Default.aspx/" هر دو به صورت "Default.aspx" در می‌آیند تا با نام ذخیره شده در دیتابیس یکسان شوند.
 

ارتباط با دیتابیس

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

پس از پیاده‌سازی کلاس‌های مرتبط برای استفاده از EF Code First، از کلاس ResourceData که در بخش اول نیز نشان داده شده بود، برای کپسوله کردن ارتباط با داده‌ها استفاده می‌شود که نمونه‌ای ابتدایی از آن در زیر آورده شده است:

using System.Collections.Generic;
using System.Linq;
using DbResourceProvider.Models;

namespace DbResourceProvider.Data
{
  public class ResourceData
  {
    private readonly string _resourceName;
    public ResourceData(string resourceName)
    {
      _resourceName = resourceName;
    }
    public Resource GetResource(string resourceKey, string culture)
    {
      using (var data = new TestContext())
      {
        return data.Resources.SingleOrDefault(r => r.Name == _resourceName && r.Key == resourceKey && r.Culture == culture);
      }
    }
    public List<Resource> GetResources(string culture)
    {
      using (var data = new TestContext())
      {
        return data.Resources.Where(r => r.Name == _resourceName && r.Culture == culture).ToList();
      }
    }
  }
}
کلاس فوق نسبت به نمونه‌ای که در قسمت قبل نشان داده شد کمی فرق دارد. بدین صورت که برای راحتی بیشتر نام منبع درخواستی به جای پارامتر متدها، در اینجا به عنوان پارامتر کانستراکتور وارد می‌شود.

نکته: درصورتی‌که این کلاس‌ها در پروژه‌ای جداگانه قرار دارند، باید ConnectionString مربوطه در فایل کانفیگ برنامه مقصد نیز تنظیم شود.

کش کردن ورودی‌ها
برای کش کردن ورودی‌ها این نکته را که قبلا هم به آن اشاره شده بود باید درنظر داشت:
پس از اولین درخواست برای هر منبع، نمونه تولیدشده از پرووایدر مربوطه در حافظه سرور کش خواهد شد.
یعنی متدهای کلاس DbResourceProviderFactory به‌ازای هر منبع تنها یکبار فراخوانی می‌شود. نمونه‌های کش‌شده از پروایدرهای کلی و محلی به همراه تمام محتویاتشان (مثلا نمونه تولیدی از کلاس DbResourceManager) تا زمان Unload شدن سایت در حافظه سرور باقی می‌مانند. بنابراین عملیات کشینگ ورودی‌ها را می‌توان درون خود کلاس DbResourceManager به ازای هر منبع انجام داد.
برای کش کردن ورودی‌های هر منبع می‌توان چند روش را درپیش گرفت. روش اول این است که به ازای هر کلید درخواستی تنها ورودی مربوطه از دیتابیس فراخوانی شده و در برنامه کش شود. این روش برای حالاتی که تعداد ورودی‌ها یا تعداد درخواست‌های کلیدهای هر منبع کم باشد مناسب خواهد بود.
یکی از پیاده‌سازی این روش این است که ورودی‌ها به ازای هر کالچر ذخیره شوند. پیاده‌سازی اولیه این نوع فرایند کشینگ در کلاس DbResourceManager به صورت زیر است:
using System.Collections.Generic;
using System.Globalization;
using DbResourceProvider.Data;
namespace DbResourceProvider
{
  public class DbResourceManager
  {
    private readonly string _resourceName;
    private readonly Dictionary<string, Dictionary<string, object>> _resourceCacheByCulture;
    public DbResourceManager(string resourceName)
    {
      _resourceName = resourceName;
      _resourceCacheByCulture = new Dictionary<string, Dictionary<string, object>>();
    }
    public object GetObject(string resourceKey, CultureInfo culture)
    {
      return GetCachedObject(resourceKey, culture.Name);
    }
    private object GetCachedObject(string resourceKey, string cultureName)
    {
      if (!_resourceCacheByCulture.ContainsKey(cultureName))
        _resourceCacheByCulture.Add(cultureName, new Dictionary<string, object>());
      var cachedResource = _resourceCacheByCulture[cultureName];
      lock (this)
      {
        if (!cachedResource.ContainsKey(resourceKey))
        {
          var data = new ResourceData(_resourceName);
          var dbResource = data.GetResource(resourceKey, cultureName);
          if (dbResource == null) return null;
          var cachedResources = _resourceCacheByCulture[cultureName];
          cachedResources.Add(dbResource.Key, dbResource.Value);
        }
      }
      return cachedResource[resourceKey];
    }
  }
}
همانطور که قبلا توضیح داده شد کشِ پرووایدرهای منابع به ازای هر منبع درخواستی (و به تبع آن نمونه‌های موجود در آن مثل DbResourceManager) برعهده خود ASP.NET است. بنابراین برای کش کردن ورودی‌های درخواستی هر منبع در کلاس DbResourceManager تنها کافی است آن‌ها را درون یک متغیر محلی در سطح کلاس (فیلد) ذخیره کرد. کاری که در کد بالا در متغیر resourceCacheByCulture_ انجام شده است. در این متغیر که از نوع دیکشنری تعریف شده است کلیدهای هر عضو آن برابر نام کالچر مربوطه است. مقادیر هر عضو این دیکشنری نیز خود یک دیکشنری است که ورودی‌های منابع مربوط به کالچر مربوطه در آن ذخیره می‌شوند.
عملیات در متد GetCachedObject انجام می‌شود. همان‌طور که می‌بینید ابتدا وجود ورودی موردنظر در متغیر کشینگ بررسی می‌شود و درصورت عدم وجود، مقدار آن مستقیما از دیتابیس درخواست می‌شود. سپس این مقدار درخواستی ابتدا درون متغیر کشینگ ذخیره شده (به همراه بلاک lock) و درنهایت برگشت داده می‌شود.

نکته: کل فرایند بررسی وجود کلید در متغیر کشینگ (شرط دوم در متد GetCachedObject) درون بلاک lock قرار داده شده است تا در درخواست‌های همزمان احتمال افزودن چندباره یک کلید ازبین برود.

پیاده‌سازی دیگر این فرایند کشینگ، ذخیره ورودی‌ها براساس نام کلید به جای نام کالچر است. یعنی کلید دیکشنری اصلی نام کلید و کلید دیکشنری داخلی نام کالچر است که این روش زیاد جالب نیست.
روش دوم که بیشتر برای برنامه‌های بزرگ با ورودی‌ها و درخواست‌های زیاد به‌کار می‌رود این است که درهر بار درخواست به دیتابیس به جای دریافت تنها همان ورودی درخواستی، تمام ورودی‌های منبع و کالچر درخواستی استخراج شده و کش می‌شود تا تعداد درخواست‌های به سمت دیتابیس کاهش یابد. برای پیاده‌سازی این روش کافی است تغییرات زیر در متد GetCachedObject اعمال شود:
private object GetCachedObject(string resourceKey, string cultureName)
{
  lock (this)
  {
    if (!_resourceCacheByCulture.ContainsKey(cultureName))
    {
      _resourceCacheByCulture.Add(cultureName, new Dictionary<string, object>());
      var cachedResources = _resourceCacheByCulture[cultureName];
      var data = new ResourceData(_resourceName);
      var dbResources = data.GetResources(cultureName);
      foreach (var dbResource in dbResources)
      {
        cachedResources.Add(dbResource.Key, dbResource.Value);
      }
    }
  }
  var cachedResource = _resourceCacheByCulture[cultureName];
  return !cachedResource.ContainsKey(resourceKey) ? null : cachedResource[resourceKey];
}
دراینجا هم می‌توان به جای استفاده از نام کالچر برای کلید دیکشنری اصلی از نام کلید ورودی منبع استفاده کرد که چندان توصیه نمی‌شود.

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

فرایند Fallback
درباره فرایند fallback به اندازه کافی در قسمت‌های قبلی توضیح داده شده است. برای پیاده‌سازی این فرایند ابتدا باید به نوعی به سلسله مراتب کالچرهای موجود از کالچر جاری تا کالچر اصلی و پیش فرض سیستم دسترسی پیدا کرد. برای اینکار ابتدا باید با استفاده از روشی کالچر والد یک کالچر را بدست آورد. کالچر والد کالچری است که عمومیت بیشتری نسبت به کالچر موردنظر دارد. مثلا کالچر fa، کالچر والد fa-IR است. همچنین کالچر Invariant به عنوان والد تمام کالچرها شناخته می‌شود.
خوشبختانه در کلاس CultureInfo (که در قسمت‌های قبلی شرح داده شده است) یک پراپرتی با عنوان Parent وجود دارد که کالچر والد را برمی‌گرداند.
برای رسیدن به سلسله مراتب مذبور در کلاس ResourceManager دات نت، از کلاسی با عنوان ResourceFallbackManager استفاده می‌شود. هرچند این کلاس با سطح دسترسی internal تعریف شده است اما نام‌گذاری نامناسبی دارد زیرا کاری که می‌کند به عنوان Manager هیچ ربطی ندارد. این کلاس با استفاده از یک کالچر ورودی، یک enumerator از سلسله مراتب کالچرها که در بالا صحبت شد تهیه می‌کند.
با استفاده پیاده‌سازی موجود در کلاس ResourceFallbackManager کلاسی با عنوان CultureFallbackProvider تهیه کردم که به صورت زیر است:
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
namespace DbResourceProvider
{
  public class CultureFallbackProvider : IEnumerable<CultureInfo>
  {
    private readonly CultureInfo _startingCulture;
    private readonly CultureInfo _neutralCulture;
    private readonly bool _tryParentCulture;
    public CultureFallbackProvider(CultureInfo startingCulture = null, 
                                   CultureInfo neutralCulture = null, 
                                   bool tryParentCulture = true)
    {
      _startingCulture = startingCulture ?? CultureInfo.CurrentUICulture;
      _neutralCulture = neutralCulture;
      _tryParentCulture = tryParentCulture;
    }
    #region Implementation of IEnumerable<CultureInfo>
    public IEnumerator<CultureInfo> GetEnumerator()
    {
      var reachedNeutralCulture = false;
      var currentCulture = _startingCulture;
      do
      {
        if (_neutralCulture != null && currentCulture.Name == _neutralCulture.Name)
        {
          yield return CultureInfo.InvariantCulture;
          reachedNeutralCulture = true;
          break;
        }
        yield return currentCulture;
        currentCulture = currentCulture.Parent;
      } while (_tryParentCulture && !HasInvariantCultureName(currentCulture));
      if (!_tryParentCulture || HasInvariantCultureName(_startingCulture) || reachedNeutralCulture)
        yield break;
      yield return CultureInfo.InvariantCulture;
    }
    #endregion
    #region Implementation of IEnumerable
    IEnumerator IEnumerable.GetEnumerator()
    {
      return GetEnumerator();
    }
    #endregion
    private bool HasInvariantCultureName(CultureInfo culture)
    {
      return culture.Name == CultureInfo.InvariantCulture.Name;
    }
  }
}
این کلاس که اینترفیس <IEnumerable<CultureInfo را پیاده‎سازی کرده است، سه پارامتر کانستراکتور دارد.
اولین پارامتر، کالچر جاری یا آغازین را مشخص می‌کند. این کالچری است که تولید enumerator مربوطه از آن آغاز می‌شود. درصورتی‌که این پارامتر نال باشد مقدار کالچر UI در ثرد جاری برای آن درنظر گرفته می‌شود. مقدار پیش‌فرضی که برای این پارامتر درنظر گرفته شده است، null است.
پارامتر بعدی کالچر خنثی موردنظر کاربر است. این کالچری است که درصورت رسیدن enumerator به آن کار پایان خواهد یافت. درواقع کالچر پایانی enumerator است. این پارامتر می‌تواند نال باشد. مقدار پیش‌فرضی که برای این پارامتر درنظر گرفته شده است، null است.
پارمتر آخر هم تعیین می‌کند که آیا enumerator از کالچرهای والد استفاده بکند یا خیر. مقدار پیش‌فرضی که برای این پارامتر درنظر گرفته شده است، true است. 
کار اصلی کلاس فوق در متد GetEnumerator انجام می‌شود. در این کلاس یک حلقه do-while وجود دارد که enumerator را با استفاده از کلمه کلیدی yield تولید می‌کند. در این متد ابتدا درصورت نال نبودن کالچر خنثی ورودی، بررسی می‌شود که آیا نام کالچر جاری حلقه (که در متغیر محلی currentCulture ذخیره شده است) برابر نام کالچر خنثی است یا خیر. درصورت برقراری شرط، کار این حلقه با برگشت CultureInfo.InvariantCulture پایان می‌‌یابد. InvariantCulture کالچر بدون زبان و فرهنگ و موقعیت مکانی است که درواقع به عنوان کالچر والد تمام کالچرها درنظر گرفته می‌شود. پراپرتی Name این کالچر برابر string.Empty است.
کار حلقه با برگشت مقدار کالچر جاری enumerator ادامه می‌یابد. سپس کالچر جاری با کالچر والدش مقداردهی می‌شود. شرط قسمت while حلقه تعیین می‌کند که درصورتی‌که کلاس برای استفاده از کالچرهای والد تنظیم شده باشد، تا زمانی که نام کالچر جاری برابر نام کالچر Invariant نباشد، تولید اعضای enumerator ادامه یابد.
درانتها نیز درصورتی‌که با شرایط موجود، قبلا کالچر Invariant برگشت داده نشده باشد این کالچر نیز yield می‌شود. درواقع درصورتی‌که استفاده از کالچرهای والد اجازه داده نشده باشد یا کالچر آغازین برابر کالچر Invariant باشد و یا قبلا به دلیل رسیدن به کالچر خنثی ورودی، مقدار کالچر Invariant برگشت داده شده باشد، enumerator قطع شده و عملیات پایان می‌یابد. در غیر اینصورت کالچر Invariant به عنوان کالچر پایانی برگشت داده می‌شود.
 
استفاده از CultureFallbackProvider
با استفاده از کلاس CultureFallbackProvider می‌توان عملیات جستجوی ورودی‌های درخواستی را با ترتیبی مناسب بین تمام کالچرهای موجود به انجام رسانید.
برای استفاده از این کلاس باید تغییراتی در متد GetObject کلاس DbResourceManager به صورت زیر اعمال کرد:
public object GetObject(string resourceKey, CultureInfo culture)
{
  foreach (var currentCulture in new CultureFallbackProvider(culture))
  {
    var value = GetCachedObject(resourceKey, currentCulture.Name);
    if (value != null) return value;
  }
  throw new KeyNotFoundException("The specified 'resourceKey' not found.");
}
با استفاده از یک حلقه foreach درون enumerator کلاس CultureFallbackProvider، کالچرهای موردنیاز برای fallback یافته می‌شوند. در اینجا از مقادیر پیش‌فرض دو پارامتر دیگر کانستراکتور کلاس CultureFallbackProvider استفاده شده است.
سپس به ازای هر کالچر یافته شده مقدار ورودی درخواستی بدست آمده و درصورتی‌که نال نباشد (یعنی ورودی موردنظر برای کالچر جاری یافته شود) آن مقدار برگشت داده می‌شود و درصورتی‌که نال باشد عملیات برای کالچر بعدی ادامه می‌یابد.
درصورتی‌که ورودی درخواستی یافته نشود (خروج از حلقه بدون برگشت مقداری برای ورودی منبع درخواستی) استثنای KeyNotFoundException صادر می‌شود تا کاربر را از اشتباه رخداده مطلع سازد.

آزمایش پرووایدر سفارشی

ابتدا تنظیمات موردنیاز فایل کانفیگ را که در قسمت قبل نشان داده شد، در برنامه خود اعمال کنید.

داده‌های نمونه نشان داده شده در ابتدای این مطلب را درنظر بگیرید. حال اگر در یک برنامه وب اپلیکیشن، صفحه Default.aspx در ریشه سایت حاوی دو کنترل زیر باشد:

<asp:Label ID="Label1" runat="server" meta:resourcekey="Label1" />
<asp:Label ID="Label2" runat="server" meta:resourcekey="Label2" />
خروجی برای کالچر "en-US" (معمولا پیش‌فرض، اگر تنظیمات سیستم عامل تغییر نکرده باشد) چیزی شبیه تصویر زیر خواهد بود:

سپس تغییر زیر را در فایل web.config اعمال کنید تا کالچر UI سایت به fa تغییر یابد (به بخش "uiCulture="fa دقت کنید):

<globalization uiCulture="fa" resourceProviderFactoryType = "DbResourceProvider.DbResourceProviderFactory, DbResourceProvider" />
بنابراین صفحه Default.aspx با همان داده‌های نشان داده شده در بالا به صورت زیر تغییر خواهد کرد:

می‌بینید که با توجه به عدم وجود مقداری برای Label2.Text برای کالچر fa، عملیات fallback اتفاق افتاده است.

بحث و نتیجه‎‌گیری

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

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

در ادامه درباره روش‌های مختلف نحوه پیاده‌سازی قابلیت به‌روزرسانی ورودی‌های منابع در زمان اجرا با استفاده از پرووایدرهای منابع سفارشی بحث خواهد شد. همچنین راه‌حل‌های مختلف استفاده از این پرووایدرهای سفارشی در جاهای مختلف پروژه‌های MVC شرح داده می‌شود.

البته مباحث پیشرفته‌تری چون تزریق وابستگی برای پیاده‌سازی لایه ارتباط با دیتابیس در بیرون و یا تولید یک Factory برای تزریق کامل پرووایدر منابع از بیرون نیز جای بحث و بررسی دارد.

منابع

http://weblogs.asp.net/thangchung/archive/2010/06/25/extending-resource-provider-for-soring-resources-in-the-database.aspx

http://msdn.microsoft.com/en-us/library/aa905797.aspx

http://msdn.microsoft.com/en-us/library/system.web.compilation.resourceproviderfactory.aspx

http://www.dotnetframework.org/default.aspx/.../ResourceFallbackManager@cs

http://www.codeproject.com/Articles/14190/ASP-NET-2-0-Custom-SQL-Server-ResourceProvider

http://www.west-wind.com/presentations/wwdbresourceprovider

مطالب
آموزش TypeScript #5
در ادامه مباحث شی گرایی در TypeScript قصد داریم به مباحث مربوط به interface و طریقه استفاده از آن بپردازیم. همانند زبان‌های دات نتی در TypeScript نیز به راحتی می‌توانید interface تعریف کنید. در یک اینترفیس اجازه پیاده سازی هیچ تابعی را ندارید و فقط باید عنوان و پارامتر‌های ورودی و نوع خروجی آن را تعیین کنید. برای تعریف اینترفیس از کلمه کلیدی interface به صورت زیر استفاده خواهیم کرد.
export interface ILogger {
  log(message: string): void;
}
همان طور در پست‌های قبلی مشاهده شد از کلمه کلیدی export برای عمومی کردن اینترفیس استفاده می‌کنیم. یعنی این اینترفیس از بیرون ماژول خود نیز قابل دسترسی است.
حال نیاز به کلاسی داریم که این اینترفیس را پیاده سازی کند. این پیاده سازی به صورت زیر انجام می‌گیرد:
export class Logger implements ILogger
{
}
یا:
export class AnnoyingLogger implements ILogger {
   log(message: string): void{
         alert(message);
     } 
}
همانند دات نت یک کلاس می‌تواند چندین اینترفیس را پیاده سازی کند.(اصطلاحا به این روش explicit implementation یا پیاده سازی صریح می‌گویند)
export class MyClass implements IFirstInterface, ISecondInterface
{

}
*یکی از قابلیت جالب و کارآمد زبان TypeScript این است که در هنگام کار با اینترفیس‌ها حتما نیازی به پیاده سازی صریح نیست. اگر یک object تمام متغیر‌ها و توابع مورد نیاز یک اینترفیس را پیاده سازی کند به راحتی همانند روش explicit emplementation می‌توان از آن object استفاده کرد.  به این قابلیت Duck Typing  می‌گویند. مثال:
IPerson {
   firstName: string;
   lastName: string;
} 
class Person implements IPerson {
  constructor(public firstName: string, public lastName: string) {
  }
}

varpersonA: IPerson = newPerson('Masoud', 'Pakdel'); //expilict
varpersonB: IPerson = { firstName: 'Ashkan', lastName: 'Shahram'}; // duck typing
همان طور که می‌بینید object  دوم به نام personB تمام متغیر‌ها ی مورد نیاز اینترفیس IPerson را پیاده سازی کرده است در نتییجه کامپایلر همان رفتاری را که با object اول به نام personA دارد را با آن نیز خواهد داشت.

پیاده سازی چند اینترفیس به صورت همزمان
همانند دات نت که یک کلاس فقط می‌تواند از یک کلاس ارث ببرد ولی می‌تواند n  تا اینترفیس را پیاده سازی کند در TypeScript نیز چنین قوانینی وجود دارد. یعنی یک اینترفیس می‌تواند چندین اینترفیس دیگر را توسعه دهد(extend) و کلاسی که این اینترفیس را پیاده سازی می‌کند باید تمام توابع اینترفیس‌ها را پیاده سازی کند. مثال:
interface IMover {
 move() : void;
}

interface IShaker {
 shake() : void;
} 

interface IMoverShaker extends IMover, IShaker {
}
class MoverShaker implements IMoverShaker {
 move() {
 }
 shake() {
 }
}
*به کلمات کلیدی extends و implements و طریقه به کار گیری آن‌ها دقت کنید.

 instanceof

از instanceof زمانی استفاده می‌کنیم که قصد داشته باشیم که یک instance را با یک نوع مشخص مقایسه کنیم. اگر instance مربوطه از نوع مشخص باشد یا از این نوع ارث برده باشد مقدار true برگشت داده می‌شود در غیر این صورت مقدار false خواهد بود.
یک مثال:
var isLogger = logger instanceof Utilities.Logger; 
var isLogger = logger instanceof Utilities.AnnoyingLogger; 
var isLogger = logger instanceof Utilities.Formatter;
Method overriding
در TypeScript می‌توان مانند زبان‌های شی گرای دیگر Method overriding را پیاده سازی کرد. یعنی می‌توان متد‌های کلاس پایه را در کلاس مشتق شده تحریف کرد. با یک مثال به شرح این مورد خواهم پرداخت.
فرض کنید یک کلاس پایه به صورت زیر داریم:
class BaseEmployee
{   
    constructor (public fname: string,public lname: string) 
    {  
    }  
    sayInfo() 
    {  
       alert('this is base class method');
    }  
}
کلاس دیگری به نام Employee می‌سازیم که کلاس بالا را توسعه می‌دهد(یا به اصطلاح از کلاس بالا ارث می‌برد).
class Employee extends BaseEmployee
{  
   sayInfo() 
     {  
        alert('this is derived class method');
     }  
}  

window.onload = () =>  
{   
    var first: BaseEmployee= new Employee();     
    first.sayInfo();  
    var second: BaseEmployee = new BaseEmployee(); 
    second.sayInfo(); 
}
نکته مهم این است که دیگر خبری از کلمه کلیدی virtual برای مشخص کردن توابعی که قصد overriding آن‌ها را داریم نیست. تمام توابع که عمومی هستند را می‌توان override کرد.
*اگر در کلاس مشتق شده قصد داشته باشیم که به توابع و فیلد‌های کلاس پایه اشاره کنیم باید از کلمه کلیدی super استفاده کنیم.(معادل base در #C).
مثال:
class Animal {
    constructor (public name: string) {
    }
}

class Dog extends Animal {    
      constructor (public name: string, public age:number)
      {
        super(name);
      }

    sayHello() {
alert(super.name);
 } }
اگر به سازنده کلاس مشتق شده دقت کنید خواهید دید که پارامتر name را به سازنده کلاس پایه پاس دادیم: کد معادل در #C به صورت زیر است:
public class Dog : Animal 
{    
      public Dog (string name, int age):base(name)
      {
      }
}
در تابع sayHello نیز با استفاده از کلمه کلیدی super به فیلد name در کلاس پایه دسترسی خواهیم داشت.

*دقت کنید که مباحث مربوط به interface و private modifier و Type safety که پیش‌تر در مورد آن‌ها بحث شد، فقط در فایل‌های TypeScript و در هنگام کد نویسی و طراحی معنی دار هستند، زیرا بعد از کامپایل فایل‌های ts این مفاهیم در Javascript پشتیبانی نمی‌شوند در نتیجه هیچ مورد استفاده هم نخواهد داشت.

ادامه دارد...