یادداشتهای یک برنامه نویس

بیان تجربیات و دیدگاه های یک برنامه نویس در مورد نرم افزار , طراحی و تولید برنامه با استفاده از تکنولوژی های مایکروسافت

یادداشتهای یک برنامه نویس

بیان تجربیات و دیدگاه های یک برنامه نویس در مورد نرم افزار , طراحی و تولید برنامه با استفاده از تکنولوژی های مایکروسافت


    تا اینجا شاید این سوال مطرح شده باشد که با وجود JQuery (که بوضوح عملیات مشابه در مقایسه با JavaScript از کیفیت ، سرعت و دقت بالاتری برخوردار است) طرح برخی جزییات در مورد JavaScript غیر ضروری و بی اهمیت باشد . اما باید توجه داشت که علیرغم اینکه JQuery مخصوصا در زمینه سازگاری با مرورگرهای مختلف و انجام عملیاتی که انتظار داریم از Syntax و روشهای بسیار ساده تری استفاده می کند ، به همان میزان برنامه نویس از بسیاری از جزییات دور نگاه داشته می شود که به نظر می رسد مخصوصا با شرایطی که امروز وجود دارد، حداقل درک بسیاری از این جزییات برای برنامه نویسانی که از تکنولوژی های ASP .NET MVC و WEB API استفاده می کنند ، ضروری است . حداقل درک عمیقی از مفاهیم Request و Response ، Synchronous و Asynchronous ، وجه تمایز بین Verb های HTTP، ساختار URL و نقش AJAX قطعا برنامه نویس را در اتخاذ روش های مناسب و کارآمد یاری داده و امکان انتخاب رویکرد صحیح را در شرایط متنوع میسر می سازد . 
مخصوصا در AJAX موضوع در Javascript کاملا با دقت و تمرکز بیشتری بررسی شده و پس از آن روشهای بسیار ساده تری که در JQuery برای انجام همین عملیات تدارک دیده شده اند معرفی خواهند شد .

در ساده ترین حالت، همه Message ها یا پیام های HTTP در دو گروه عمده Request و Response قابل تقسیم بندی هستند . واضح است که چنانچه از نام آنها نیز مشخص می باشد Request عبارت است از یک درخواست که اطلاعات دقیق تر این درخواست در قالب یک پکیج اطلاعاتی شامل Request Header و Request Body جهت دریافت محتوای اطلاعاتی خاص (Contents) یا انجام عملیات خاص (Actions) به یک سرویس دهنده ارسال می شود . آدرس یا مشخصه یکتایی (Unique) سرویس دهنده در URL و به طور دقیق تر در Request Header قرار می گیرد . 
همچنین اطلاعات ارسال شده در این پکیج شامل یک مشخصه خاص به نام Method یا Verb می باشد. بر اساس اطلاعات ارائه شده پروتکل HTML 1.1 در RFC 2616 ، از مهمترین انواع Method ها می توان به GET و POST اشاره کرد . بر اساس استاندارد مطرح شده توسط W3C تعداد Method ها در واقع بیش از این دو می باشد که در حال حاضر ذکر آنها ضروری نیست . برای اطلاعات دقیقتر می توانید به مستندات W3C و بخشهای مربوط به تقسیم بندی این متدها در RFC 2616 در زیر:

 

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

 

مراجعه نمایید.
امروزه همه مرورگرهای مدرن از هر دو متد GET و POST پشتیبانی می کنند و در مورد سایر متدها اختلاف هایی وجود دارد که جزییات آن در مستندات هر مرورگر وجود دارد . 
قبل از توصیف مختصری در مورد این دو متد و تفاوتهای آنها لازم است توجه داشته باشیم که تایپ URL یا آدرس در Address Bar مرورگر یا کلیک بر روی یک لینک، در حقیقت نمونه های بارز و رایجی از آغاز یک در خواست با متد GET می باشند که از این به بعد آن را Get Request می نامیم . در خواست POST معمولا در مرور گر توسط ارسال (Submit) پکیج اطلاعات یک فرم قابل انجام می باشد که از این به بعد آن را POST Request می نامیم . در اینجا فرض بر این است که خواننده این سطور کاملا با مفهوم Form در HTML و استفاده از Attribute های Action و Method آشنا می باشد . به طور دقیق تر Attribute مشخص شده با نام Method در داخل تگ FORM مشخص کننده نوع متد مورد نظر جهت ارسال پکیج اطلاعات Form به URL یا آدرسی است که در Action ذکر شده است .

نکته:

نظر به اهمیت استفاده از همه ظرفیت های پروتکل HTTP در WEB API ، موضوع توجه و تمایز بین Verb های مستند شده در HTTP 1.0 بسیار اهمیت دارد . حتی در ASP .NET MVC نیز در مقایسه با ASP .NET Web Form موضوع توجه به ویژگیها و موارد استفاده از Verb ها بسیار پر رنگ تر از گذشته مورد توجه برنامه نویسان می باشد . اما در WEB API درک دقیق Verb ها و چنانچه اشاره شد استفاده کامل از حداکثر ظرفیتهای HTTP از اولویت بسیار بالایی برخوردار است . به عبارت دیگر معماری REST حساسیت بسیار ویژه ای نسبت به Verb های متنوع پروتکل HTTP مخصوصا Get ، Post ، Put و Delete داشته و حتی یک مراجعه سطحی به مستندات Web API بوضوح نشان می دهد که اساسا سرویسهای RESTFull بر همین مبنا پایه گذاری شده اند . به طور دقیق Verb های HTML تر در WEB API از یک دیدگاه بسیار خاص )نسبت به نوع عملکرد آنها) طبقه بندی شده اند و در این میان، عامل اصلی تعیین کننده نوع Verb، ساختار ظاهری URL می باشد که بر اساس ساختار اطلاعاتی هدف (اینکه یک آیتم یا یک لیست یا مجموعه باشد) مشخص می گردد. با این توضیحات و با در نظر گرفتن چهار Verb ذکر شده ، می توان به سادگی دریافت که هشت عملیات مختلف و متنوع ، توسط کنترلر های Web API قابل انجام است . مجددا تکرار می کنم که این موضوع کاملا وابسته به ساختار URL می باشد . تقسیم بندی خاصی که پیشتر به آن اشاره شد مفهوم ویژه ای به نام Idempotency را مطرح می سازد که اشاره به این واقعیت مهم است که تکرار عملیات (به عنوان مثال عملیات ناشی از صدور فرمان Refresh یا کلید F5) در Verb های Get و Put و Delete منجر به انجام عملیات یکسانی می گردد و تکرار آن به هیچ وجه مضر یا مخرب نیست . در یک مثال ساده با هربار تکرار URL و آدرس فرضی


http://Nowhere.com/Customers /12345


زیر (که بوضوح می تواند به عنوان یک Get Request به منظور دریافت جزییات اطلاعات مربوط به مشتری (Customer) خاصی با ID و شناسه 12345 تلقی شود) ، آنچه هر بار تکرار خواهد شد تکرار ارائه جزییات اطلاعات یک مشتری با مشخصه CustomerID=12345 خواهد بود . از این جهت این Verb (ها) اصطلاحا Idempotent نامیده می شوند . 
در یک مورد مشابه دیگر اگر URL مثال قبلی را این بار از نوع Put Request در نظر بگیریم (که می تواند به صورت ایجاد یک Instance یا نمونه جدید از یک مشتری با مشخصه CustomerID=12345 تلقی شود) مجددا تکرار این عملیات در نهایت منجر به ایجاد یک نمونه از اطلاعات مشتری خواهد شد که مجددا از همین رو Put یک Verb از نوع Idempotent در نظر گرفته می شود .در مورد Delete هم دقیقا همین وضعیت وجود دارد و از ذکر مثال خودداری شده است. در مجموع این اصطلاح به تعبیری اشاره به Safe بودن یا ایمن بودن ذاتی نوع عملیاتی است که توسط Get و Put و Delete انجام می شود . در مقابل POST هیچگاه به عنوان یک Verb از نوع Idempotent در نظر گرفته نمی شود . چنانچه مطلع هستید استفاده از Post به صورت رایجی در مواقعی است که نیاز به ایجاد یک نمونه جدید از یک Object خاص وجود دارد که بر خلاف وضعیت تشریح شده در Put انتظار داریم سیستم خود یک ID معتبر تولید کرده و (بر اساس سایر اطلاعات تکمیلی که Post شده است) اقدام به ایجاد نمونه جدید بنماید . بدیهی است که تکرار این عملیات مخرب و با هر بار اجرا یک نمونه جدید (بر خلاف مثال های قبل) ایجاد خواهد شد . 
 

ذکر این جزییات فقط تاکید بر این نکته بسیار مهم است که به نظر می رسد باید یک تغییر یا تجدید نظر کلی در نگرش به Verb ها و نوع استفاده از آنها برای برنامه نویسانی که قصد استفاده موثر از Asp .NET MVC و WEB API را دارند ، ایجاد شود . 
در مورد جزییات تمایز بین Verb ها در MVC در ادامه مثالهایی ارائه خوهد شد و ملاحظه خواهیم کرد که بر خلاف ASP .NET Web Form که اجبار به استفاده از یک Form و تشریک مساعی مکانیسم Page Rendering/Server Controls از سوی سیستم تحمیل میشد، در MVC امکان استفاده از چندین Form با Verb های متنوع و حتی استفاده از Asynchronous Request بواسطه استفاده از تکنولوژی AJAX ، شاهد افزایش چشمیگری در کیفیت خواهیم بود.
 

ملاحظاتی در باره Get و Post

در ساده ترین شکل می توان چنین گفت که استفاده از Get Request در مجموع باید صرفا به منظور دریافت (get) اطلاعات (در فرمت های متنوع HTML ، XML ، JSON و ...) از web Server تدارک دیده شود . در مقابل یک Post Request به طور معمول انحصارا برای تغییر (Modify) در اطلاعات (اشاره به Insert و Update و Delete) مورد استفاده قرار می گیرد .
تنها و تنها برنامه نویس مسئول انتخاب استراتژی و انتخاب Verb مناسب ، بعد از تشخیص کامل Idempotent بودن یا Safe بودن یا غیر مخرب بودن عملیات است . عملیاتی نظیر جسجتو در Database یا دریافت گزارش از واضح ترین مصادیق استفاده صحیح از Get Request می باشند . در هنگام انتخاب Get Request موارد زیر باید همیشه مورد توجه باشند :

1- امکان Cache شدن نوع درخواست Get (بر خلاف Post) وجود دارد.
2- در شرایط طبیعی، همواره سابقه درخواست های Get در Browser History ثبت می شوند .
3- امکان نشانه گذاری و Bookmark درخواست های Get وجود دارد .
4- در صورت تمایل به سادگی امکان به اشتراک گذاری (Sharing) در خواست های نوع Get وجود دارند .
5 درخواستهای نوع Get به سادگی قابل دستکاری ، سو استفاده و واضح تر عرض کنم Hack شدن می باشند . بنابراین در هنگام طراحی سیستم پردازش درخواست های Get باید از مکانیسم های مناسب جهت کنترل این ویژگی استفاده کنید که متاسفانه اینجا مجال پرداختن به آنها نیست .
6- مکانیسم درخواست های Get به ترتیبی است که اطلاعات تکمیلی مورد نیاز برای پردازش (در صورت نیاز) در خود URL و توسط مجموعه QueryString به سمت Server هدایت می شود . بدیهیست که معنای دقیقتر آن این است که این مکانیسم به هیچ وجه نباید در ارسال اطلاعات حساسی مثل کلمه عبور ، مشخصات کارتهای اعتباری و ... مورد استفاده قرار بگیرد .

نکته:


بر خلاف یک نظریه غلط که Post Request را روش ایمنی برای موارد مذکور تلقی می کنند ، باید توجه داشت که اصولا موضوع امنیت اساسا هیچ ارتباطی با انتخاب Verb نداشته و باید در نظر گرفت که در Post Request اطلاعات صرفا در بدنه Request قرار می گیرند که تقریبا به همان سهولت روش Get می تواند توسط افراد سوجو مورد سواستفاده قرار بگیرد . 


7- با عنایت به مطلب اشاره شده در آیتم ششم، هر چند هیچ محدودیتی در پروتکل Http جهت تعیین حداکثر اندازه یا تعداد کاراکترهای Query String تعیین نشده اما محدودیت ویژه ای از طرف خود مرورگرها برای این ویژگی وجود دارد . در IE این مقدار حداکثر 2048 (با احتساب سایر مولفه ای موجود در URL) کاراکتر می باشد .
8- در صورت استفاده از Javascript یا ترجیحا JQuery امکان ارسال Asynchronous Get Request با استفاده از تکنولوژی AJAX وجود دارد که مثالهایی از آن در ادامه ارائه خواهد شد . در JQuery متدهای get و getJSON و getScript و load به همین منظور تدارک دیده شده اند .

خلاصه ای از مواردی که در Post Request باید به آن توجه شود به قرار زیر است :

1- در صورتیکه حجم اطلاعاتی نیاز دارید به Server ارسال کنید زیاد باشد بهتر است که روش Post را انتخاب کنید .
2- در صورت نیاز به ارسال اطلاعات حساسی مثل Password و مشخصات کارت های اعتباری و اطلاعات حسابهای بانکی از روش Post (به همراه اطمنیان از سایر مولفه های امنیتی مثل SSL و پروتکل Https ) استفاده نمایید .
3- در شرایط معمولی یک Post Request معمولا با استفاده از ساختار و تگ Form با ذکر Method=Post در HTML میسر می باشد. اما این لزوما به این معنی نیست که تگ Form صرفا برای آغاز یک Post Request کاربرد دارد . بدیهی است که با تغییر در Method امکان تغییر به Get نیز وجود خواهد داشت . چنانچه در ادامه شاهد خواهیم بود با استفاده از AJAX تقریبا امکان استفاده از همه انواع Verb ها وجود دارد .
4- در JQuery متد post برای ایجاد یک Asynchronous Post Request با استفاده از AJAX تدارک دیده شده است .

۲ نظر موافقین ۰ مخالفین ۰ ۰۸ فروردين ۹۴ ، ۱۷:۱۴
مهران حسین نیا

بعد از گذشت زمان نسبتا طولانی از نوشتن آخرین مطالبی که هر از گاهی در وبلاگ یا فیسبوک نوشته میشد ، مدتها بود موضوع روند تکامل Form ها در HTML از زمان به کار گیری Javascript به عنوان ابزاری برای کنترل "صحت مقدار یا فرمت مقادیر" ، تا زمان حاضر که شاهد یک همکاری و هارمونی بسیار شگفت انگیز بین JQuery و MVC هستیم ، بخشی از ذهن مرا به خود مشغول ساخته بود.
مخصوصا دغدغه مرور مختصری بر مفهوم و کاربرد FORM در MVC و ارتباط آن با مفاهیم بسیار مهم تری به نام AJAX و Partial View ابعاد این موضوع را تا حد نسبتا غیر قابل کنترلی (حداقل از دیدگاه من) افزایش می داد .
به عبارت ساده تر به نظرم رسید حتی یک معرفی سطحی و اجمالی بر موضوعات مذکور ممکن است بسیار فراتر از مجال محدودی باشد که در اختیار من قرار دارد. مجموعه عناوین و مفاهیمی که ذکر شد بسیار گسترده ولی انصافا بسیار مهم و ظریف بوده و حاوی نکاتی هستند که یقین دارم تحقیق در مورد آنها ابعاد تازه و جالب تری را به این مجموعه اضافه خواهد نمود . 
اگر به روزهای ابتدایی HTML و در واقع زمان معرفی اولین Browser یا مرورگر مخصوص وب با نام Mozaic در سال 1993 برگردیم ، بنا بر مستندات مستدل موجود این مرور گر که در واقع جد همه مرورگرهای مدرن امروزی محسوب میشود ، قابلیت برقراری یک ارتباط محاوره ای با User را با استفاده از تگ Form برای کاربر فراهم می کرده است . 
در واقع یک سال بعد یعنی در سال 1994 وب که حالا تقریبا 4 ساله شده بود توجه بسیاری از بازیگران عصر طلایی صنعت IT را به خود جلب کرد و در سپتامبر همان سال Internet Engineering Task Force با نام اختصاری IETF که متولی ، ناشر و حامی غیر انتفاعی استانداردهای اینترنت بود، کار گروه ویژه ای را برای تدوین و انتشار استانداردهای HTML منسوب کرد. از اعضای مهم این گروه می توان به Tim Berners-Lee (مخترع وب) ، Don Connelly و Mark Anderson اشاره کرد که حاصل این اقدام انتشار استاندارد HTML 2 و تهیه Document Type Definition مربوط به این استاندارد بود .
جالب است بدانیم که Netscape Navigator که به عنوان اولین مرورگر تجاری موفق در سیلیکون ولی تدوین و منتشر شد حاصل کار گروهی Mark Anderson و Jim Clark بود. همانطور که قبلا هم اشاره شد، از همان ابتدا عنصر Form یکی از اصلی ترین و پر کاربرد ترین عناصر و یا Element های HTML محسوب میشد و با توجه به محدودیت های پهنای باند و بنا بر ویژگیها و محدودیت هایی که پروتکل HTTP بر وب تحمیل می کرد ، تا قبل از 1995 موضوع بررسی یا کنترل صحت مقادیر و اطلاعات Form که به نام Form Validation شناخته می شود منحصرا صرفا با یک Round Trip (اشاره به Post Back و Initiate و آغاز Request از سوی مرورگر ، پردازش Request در Server و ایجاد Response ) میسر بود . به عبارت ساده تر ، امکان انجام این کنترل صرفا در Server و با استفاده از زبانهایی مثل PERL وجود داشت که با توجه به سرعت مودم های 28.8 kbps ، نیاز به زمان قابل توجهی داشت . اولین بار Brenden Eich که از مهندسین ارشد Netscape بود به فکر انجام این کنترل، تمام با استفاده از یک زبان اسکریپت افتاد و این انگیزه سبب طراحی زبان اسکریپتی گردید که ابتدا Mocha و در فاصله کوتاهی LiveScript نامیده شد. قبل از تولید نسخه تجاری Netscape Navigator 2 در سال 1995 بازیگر مطرحی در عرصه تکنولوژی مثل Sun Microsystem در تکمیل طراحی LiveScript پا به عرصه وجود گذاشت و نتیجه این همکاری تولد زبان JavaScript بود که با یک تاخیر زمانی کوتاه به Netscape Navigator 3 ملحق شد و از آن موقع تا زمان حاضر چنانچه همه ما مطلع هستیم، به عنوان یکی از ملزومات اصلی مروگرها شناخته می شود. 
اما در این میان همه چیز آن چنان که انتظار میرفت پیش نرفت . چرا که Microsoft به سرعت شروع به تدوین مرورگر اختصاصی و معلوم الحال خود نمود که علاوه بر داغ تر کردن تنور رقابت بازار مروگرها، به دلیل پرهیز از مشکلات ناشی از ثبت انحصاری Javascript نسخه انحصاری خود از زبان اسکریپتی اختصاصی خود به نام Jscript را ضمیمه مرورگر خود کرد و روانه بازار نمود . این موضوع در واقع مبدأ آشفته بازاری بود که انگیزه نیاز به یک استاندارد جامع در زبانهای اسکریپت را تقویت نمود و در نهایت منجر به تدوین ECMAScript ( به صورت رایجی ، اکما - اسکریپت تلفظ می شود) به عنوان استاندارد پایه و مبنای زبانهای اسکریپت شد . 
نکته مهم:
از آنجاییکه JQuery و نقش آن در DOM Processing و Form Validation و Form Submission و نهایتا AJAX بسیار حائز اهمیت است، لاجرم لازم می دانم که که در این مقاله ، هر چند به اجمال، این مباحث بیشتر بررسی شده تا احیانا برخی از ابهامات موجود در این زمینه برطرف گردد .

با در نظر گرفتن مطالبی که عنوان شد از اینجا به بعد در واقع Javascript بر خلاف یک تلقی اشتباه (که برخی آن را مترادف با ECMAScript قلمداد می کنند) یک پیاده سازی از استاندارد از ECMAScript است و در حقیقت شامل اجزای زیر می باشد :
1- هسته و Core مربوط به ECMAScript .
2- Document Object Model یا به اختصار DOM
3- پشتیبانی از Browser Object Model یا به اختصار BOM

به منظور درک عمیقتر ECMAScript اولا باید آن را مستقل از Web در نظر گرفت. در واقع مرورگرهای وب یک میزبان برای هسته اصلی ECMAScript می باشند که امکان پیاده سازی سطوح بالاتر ، بر مبنای امکانات پایه ای که فراهم می کند را ، میسر می سازند . مواردی نظیر نحو یا Syntax ، امکان تعریف Type ، کلید واژه ها، اپراتور ها ، مفهوم Object و ... در JavaScript همه به لطف پیاده سازی سطوح پایین تر در ECMAScript ایجاد و بنا بر نیاز موجود تقویت و بهینه شده اند . با مراجعه به منابع معتبر می توان بوضوح مشاهده کرد که ECMAScript در طول حیات خود و با گذشت زمان و قطعا متناسب با نیازهای موجود ، دارای هسته ای به مراتب مجهز تر شده است که به نظرم در اینجا مجال پرداختن به جزییات آن وجود ندارد و علاقمندان می توانند با مراجعه به منابع معتبر جزییات دقیق تر این تکامل تدریجی ECMAScript را مطالعه کرده و در جریان کامل آن قرار بگیرند . در این زمینه، پشتیبانی از دستورات try catch ، پشتیبانی دقیق تر از کلاسها و مفهوم Inheritance و وراثت ، پشتیبانی و پیاده سازی JSON به عنوان فرمتی برای تبادل Object ها و اطلاعات و .... از مهمترین مواردی بودند که به تدریج در چرخه تکامل ECMAScript نقش آفرینی کرده اند .
مفهوم Document Object Model یا DOM
این مفهوم در اصل عبارت است از یک API خاص منظوره برای کار با فرمت XML که بعدها به منظور کار با HTML تقویت ، ویرایش و یا گسترش یافته است . 
در مقطعی از زمان مرورگرهای اختصاصی Netscape و Microsoft هر یک روایت اختصاصی خود از DHTML را که لزوما با یکدیگر سازگار نبوده و حتی تضادهایی هم داشت، ارائه نموده و این موضوع یکی از عوامل اصلی نیاز به یک استاندارد فراگیر را مطرح کرد که موجب شد World Wide Web Consortium یا W3C تدوین استانداردهای DOM را در دستور کار خود قرار دهد .
از دیدگاه DOM یک سند XML یا HTML (در واقع HTML یک زیر مجموعه از XML محسوب می شود) صرفا دارای مجموعه ای از اجزا یا المانها یا Node هایی است که به صورت سلسله مراتبی (Hierarchical) ، به ترتیبی قرار دارند که اولا همه اجزا (یا همان المانها و Node ها) منشعب از یک Node ویژه به عنوان Root و ریشه بوده و ثانیا هر Node می تواند از نظر محتوا شامل یک یا چند Node فرزند (یا هیچ) بوده و از نظر نوع می تواند شامل انواع متفاوتی از اطلاعات یا Data باشد .
اتخاذ این استراتژی، API را قادر می سازد تا عملیاتی نظیر پیمایش (Traverse) ، افزودن (Add) ، حذف (Remove) ، الحاق (Append) و ... را به صورت بسیار موثر و کارآمدی را انجام دهد . 
اصطلاحات DOM Level 1 ، DOM Level 2 و DOM Level 3 اشاره به روایت های متعددی است که از سال 1998 تا امروز استانداردهای DOM را بر حسب شرایط روز تدوین و منتشر نموده است . در حقیقت مواردی مثل حمایت از Device هایی همچون Mouse ، الحاق و پشتیبانی Css و در ادامه تجهیز و گسترش Event ها و الحاق استاندارد XPath همه و همه از مواردی بودند که به مرور زمان در DOM ایجاد شده و منتشر شدند . 
در حال حاضر به لطف Library های مشهور و کاربردی و در صدر آنها JQuery حمایت و قابلیت کار با DOM تا بالاترین سطح خود در مرورگرها گسترش پیدا کرده و در ادامه و مخصوصا در مباحث مربوط به AJAX مثالهای کاربردی تری از آن را ملاحظه خواهیم کرد .
در واقع به نظرم تا اینجا خواننده این سطور باید به این نکته مهم توجه داشته باشد که JavaScript و JQuery (به عنوان یک کتابخانه کاربردی بنا شده بر اساس Javascript) چگونه در طول زمان بر اساس نیازهای موجود از یک ابزار اسکریپت نویسی ساده به منظور کنترل Form Validation و Dom Traversal (اشاره به قابلیت های آن در ارتباط با پیمایش ، حذف، الحاق و ... Node ها در ساختار سلسله مراتبی DOM) به یک جزء لاینفک از Platform های وب مخصوصا ASP .NET MVC تبدیل شد .
مفهوم Browser Object Model یا BOM
چنانچه پیشتر نیز اشاره شد، اولین و مهمترین ویژگی Javascript پشتیبانی کامل از ECMAScript و DOM و BOM می باشد . به طور دقیق تر مفهوم BOM اشاره به مجموعه ای از امکاناتی بود که همزمان با Netscape Navigator 3 و Internet Explorer 3 در این دو مرورگر ایجاد شد که امکان کار با Window یا پنجره میزبان مرورگر و همچنین استفاده از Dialog های Modalو Modeless را فراهم نمودند . به طور مشخص تمرکز BOM در کار با خود Object پنجره میزبان (Window) و Frame های این پنجره قرار داشت و به طور کلی شامل امکاناتی برای کار با سایر پنجره های Popup ، امکان قابلیت حرکت، تغییر اندازه و بستن پنجره ها و امکان کار با Object های کاربردی navigatorو location و Screen و از همه مهمتر (و در بحث ما با تمرکز بیشتر) تولد XMLHttpRequest به عنوان محور اصلی ظهور تکنولوژی AJAX و گسترش آن بود .

۰ نظر موافقین ۰ مخالفین ۰ ۰۸ فروردين ۹۴ ، ۱۷:۰۷
مهران حسین نیا

در خلال تهیه یک مقاله با موضوع مقایسه تکنولوژی های Web Service در مایکروسافت از سرویس های موشوم به ASMX-based Web Services تا Web API با پوستری مواجه شدم که به نظرم بسیار مفید و مختصر بود و در غالب یک دیاگرام مسیر پردازش Request در ASP .NET Processing Pipeline و مراحل و نحوه ایجاد Response را نمایش می داد. 
نسخه اریجینال این پوستر در فرمت PDF و همچنین نسخه ای شامل ترجمه ها و شرحی به زبان فارسی در فرمت PDF و در نهایت یک تصویر Real Size از این پوستر در فرمت JPG در لینک مستقیم زیر قابل دانلود می باشد .



Download Link: https://onedrive.live.com/redir?resid=FC40347DCF4AAF1D%211394

 

۰ نظر موافقین ۰ مخالفین ۰ ۰۱ آبان ۹۳ ، ۱۵:۵۲
مهران حسین نیا
چهارشنبه, ۳۰ مهر ۱۳۹۳، ۰۸:۵۸ ب.ظ

هرگز UI را منتظر نگذاریم (از آرشیو در 11 ژانویه 2014)

این مطلب در چهار قسمت ابتدا در فیسبوک منتشر شد و در اینجا هر 4 قسمت  در غالب یک مطلب واحد الحاق گردید.

 
وقتی Life-Cycle یک Windows 8 Store Application را مرور میکنیم متوجه میشویم که مجددا تفاوت های محسوسی در مدلهای مرتبط با Asynchronous Programming ایجاد شده است. این تغییر مدل تابع تغییرات بی شماریست که مخصوصا به موازات افزایش قابلیتهای پردازنده ها ، خوشبختانه یا متاسفانه ما نیز مجبور به فراگیری جزییات این تغییرات محسوس هستیم.

اما در مجموع اینک در Windows 8 حداقل یک معیار مشخصی برای انتخاب زمان درست و مناسب استفاده از این مدل در اختیار داریم. همه ما به خوبی میدانیم که معماری Metro Style در Windows 8 مقدمات کوچ به حذف Device های سنتی Keyboard و Mouse را فراهم کرده و زمینه را برای ترغیب کاربران به استفاده از صفحات قابل لمس نمایشی را در PC های امروز و آینده فراهم نموده است.
در این معیار صریحا عنوان شده است که برای عملیاتی که پیش بینی میشود بیش از 50 میلی ثانیه وقت پردازنده را اشغال خواهند کرد حتما باید از مدل Asynchronous استفاده نمود. اما بررسی سیر تغییرات این مدل، خالی از لطف نیست.
در مجموع در .Net Platform ما با سه انتخاب روبرو هستیم:

1 – مدل Asynchronous Programming Model که به اختصار APM نامیده میشود.
2- مدل Event-based Asynchronous Programing یا EAP.
3- مدل Async-Await که تا جاییکه بنده مطلع هستم هنوز نام اختصاری ندارد!.
 

مدل APM

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

در این مدل، برنامه نویس به طور معمول عملیات زمان بر (مایکروسافت بیشتر در این باره از اصطلاح Expensive استفاده میکند که بدون شک ترجمه آن هیچ کمکی به خوانندگان زبان فارسی نمیکند) را در یک جفت BeginXXX / EndXXX محصور میکند و نتیجه یا محصول عملیات مذکور را جداگانه با تخصیص یک Callback (که یک Handle به Result مورد نظر را ارائه میکند) دریافت نموده و روال طبیعی ادامه پیدا میکند.

محصول اصلی مدل Asynchronous مذکور که برنامه نویسان مشتاقانه منتظر دریافت نتیجه آن میمانند یک Interface آشنا و بسیار مشهور به نام IAsyncResult است که با دقت در نام آن متوجه میشویم که مطابق معمول مایکروسافت حتی در انتخاب نام آن بسیار هوشمندانه و ابتکاری عمل کرده است.

با مراجعه به MSDN در می یابیم که این Interface دارای تعداد چهار Properties است و به طور دقیق تر می توان چنین گفت که این Interface تعداد چهار Properties را ارائه یا Expose نموده است که به ترتیب عبارتند از :

 

AsyncState
AsyncWaitHandle
CompletedSynchronously
IsCompleted

 

اینترفیس IAsyncResult به طور معمول در کلیه کلاسهایی مثل FileStream.BeginRead که حاوی متدهایی با قابلیت اجرای Asynchronous باشند، Implement شده است.
"من شدیدا از ترجمه این اصطلاحات وحشت دارم و شاید این ترس ناشی از عدم تسلط کافی و لازم بر زبان فارسی باشد. اما معتقدم که آن مفهومی که مثلا در مواجهه با اصطلاحی نظیر Implement به ذهن برنامه نویس متبادر میشود با هیچ توضیحی قابل قیاس نیست و درک اصلی این مفهوم با استفاده از تجربه از اهمیت بیشتری برخوردار است حتی اگر متهم به رواج زبان بیگانه بشویم."
در ادامه بحث نحوه عملکرد مدل APM باید این نکته را هم اضافه کرد که یک Instance یا نمونه IAsyncResult ، همزمان با خاتمه عملیات مورد نظر ما به یک delegate ویژه به نام AsyncCallback نیز ارسال یا پاس میشود. این مفاهیم با یک مثال جنبه بسیار ملموس تری پیدا خواهند کرد:

کلاس FileStream دارای جفت متدهای BeginRead و EndRead میباشد که جهت انجام Asynchronous همان وظایفی که متد Read به صورت Synchronous انجام میداده ،(یعنی قرائت و خواندن بایت به بایت اطلاعات فایل مورد نظر) پیش بینی شده اند.
بر خلاف Read اینک در BeginRead به هیچ وجه پردازش متوقف نشده و عملیات پیش بینی شده بعد از BeginRead به همان شکل طبیعی ادامه خواهد یافت. اما CLR در پشت پرده، با ایجاد یک Thread جداگانه وظیفه خواندن Byte به Byte از فایل را آغاز میکند.
به ازای هر BeginRead یک EndRead باید تدارک دیده شود تا بتوان با کنترل کامل این عملیات نتیجه عملیات را (در این مثال بایتهای خوانده شده از فایل) دریافت نمود.
چنانچه قبلا اشاره شد نتیجه اجرای BeginRead ایجاد یک Interface از نوع IAsyncResult است که کلیه اطلاعاتی که ما برای کنترل کامل این پروسه نیاز داریم را در بر دارد.
از سوی دیگر قطعا BeginRead (و اساسا هر BeginXXX دیگری) دارای همان پارامترهایی خواهد بود که روایت Asynchronous این متد (یعنی Read) از آن استفاده میکرده است.
(البته باید توجه داشت که در صورت وجود پارامترهایی از نوع out ، امکان وجود این پارامترها در روایت Asynchronous وجود ندارد).

اما متد BeginRead دارای دو پارامتر اضافی دیگر نیز هست که اولین آنها همان دوست و آشنای قدیمی خود ما یعنی AsyncCallback و دومی پارامتریست که در بین برنامه نویسان اصطلاحا به State معروف می باشد و به طور دقیق یک User-defined Object می باشد.
اما بشنویم از قسمت دوم این ماجرا یعنی EndRead که در این لحظه میتوان گفت که اولا به طور مشخص پایان دهنده عملیات Asynchronous می باشد و ثانیا دارای مقدار بازگشتی همان متد نظیر خود یعنی Read خواهد بود.(در مثال ما یعنی EndRead تعداد بایتهای خوانده شده مقدار بازگشتی این متد می باشد)
متد EndRead علاوه بر پارامترهای متد نظیر خود، دارای یک پارامتر از نوع IAsyncResult نیز هست و فقط برنامه نویسان واقعی میدانند که این چقدر خبر خوشایند و مسرت بخشی است!!.

لطفا به کد مثالی که عینا از MSDN استخراج شده توجه نمایید:

 

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections.Specialized;
using System.Collections;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
public class UseDelegateForAsyncCallback
{
static int requestCounter;
static ArrayList hostData = new ArrayList();
static StringCollection hostNames = new StringCollection();
static void UpdateUserInterface()
{
// Print a message to indicate that the application
// is still working on the remaining requests.
Console.WriteLine("{0} requests remaining.", requestCounter);
}
public static void Main()
{
// Create the delegate that will process the results of the
// asynchronous request.
AsyncCallback callBack = new AsyncCallback(ProcessDnsInformation);
string host;
do
{
Console.Write(" Enter the name of a host computer or <enter> to finish: ");
host = Console.ReadLine();
if (host.Length > 0)
{
// Increment the request counter in a thread safe manner.
Interlocked.Increment(ref requestCounter);
// Start the asynchronous request for DNS information.
Dns.BeginGetHostEntry(host, callBack, host);
}
} while (host.Length > 0);
// The user has entered all of the host names for lookup.
// Now wait until the threads complete.
while (requestCounter > 0)
{
UpdateUserInterface();
}
// Display the results.
for (int i = 0; i< hostNames.Count; i++)
{
object data = hostData [i];
string message = data as string;
// A SocketException was thrown.
if (message != null)
{
Console.WriteLine("Request for {0} returned message: {1}",
hostNames[i], message);
continue;
}
// Get the results.
IPHostEntry h = (IPHostEntry) data;
string[] aliases = h.Aliases;
IPAddress[] addresses = h.AddressList;
if (aliases.Length > 0)
{
Console.WriteLine("Aliases for {0}", hostNames[i]);
for (int j = 0; j < aliases.Length; j++)
{
Console.WriteLine("{0}", aliases[j]);
}
}
if (addresses.Length > 0)
{
Console.WriteLine("Addresses for {0}", hostNames[i]);
for (int k = 0; k < addresses.Length; k++)
{
Console.WriteLine("{0}",addresses[k].ToString());
}
}
}
}

// The following method is called when each asynchronous operation completes.
static void ProcessDnsInformation(IAsyncResult result)
{
string hostName = (string) result.AsyncState;
hostNames.Add(hostName);
try
{
// Get the results.
IPHostEntry host = Dns.EndGetHostEntry(result);
hostData.Add(host);
}
// Store the exception message.
catch (SocketException e)
{
hostData.Add(e.Message);
}
finally
{
// Decrement the request counter in a thread-safe manner.
Interlocked.Decrement(ref requestCounter);
}
}
}
}


مدل EAP

از این مدل به عنوان برادر مدل APM یاد میشود و همانطور که از نام آن مشخص میباشد، Event Handling در آن نقش کلیدی ایفا میکند و نتیجه عملیات Asynchronous در قالب یک Callback همواره با استفاده از Event Handling قابل دریافت است.

یکی از نشانه های بارز آن زمانیست که با وضعیتی مانند زیر روبرو میشویم:
DoSomethingComplete += ExecutionEndFunction();

برنامه هایی که چندین وظیفه (Tasks) را به صورت همزمان (Simultaneously) بدون منتظر گذشتن UI انجام میدهند، اغلب نیازمند به استفاده از تکنیک Multiple Threads هستند .
هر Thread (که من عمدا از ترجمه آن به معادل "رشته" پرهیز میکنم) در واقع کوچکترین واحد اجرای عملیات (Tasks) در پردازنده محسوب میشود که معمولا در یک Process اختصاص یافته از سوی سیستم عامل وظایف محول شده را به انجام می رساند. از سوی دیگر یک Process میتواند شامل چندین Thread جداگانه باشد که این Threadها در کنار هم و در قالب یک نظم و منطق مشخصی وظایف مختلف منتسب به Process مذکور را انجام میدهند.
درست نظیر همه مباحث دیگر تکنیکی برنامه نویسی، به نظر میرسد در اینجا نیز زمینه برای انحراف از موضوع اصلی فراهم شده است.
اما بحث Thread ها خود بحث بسیار فنی، مفصل و جدا گانه ایست و من دوباره صرفا به یک مثال بسیار ساده و مختصر در اینجا اکتفا خواهم نمود.

 

using System;
using System.Threading;
class ThreadTest
{
static void Main()
{
Thread t = new Thread (WriteY);
t.Start();
// اجرای هم زمان یک وظیفه در Thread اصلی برنامه
for (int i = 0; i < 1000; i++) Console.Write ("x");
}
static void WriteY()
{
for (int i = 0; i < 1000; i++) Console.Write ("y");
}
}


به جای هر توضیح اضافی، مرور خروجی این برنامه خود بازگو کننده کل عملیات ساده ایست که در این برنامه انجام میشود:

 

xxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

 

ابتدا Thread اصلی برنامه یک Thread دیگر به نام t ایجاد میکند که Constractor کلاس Thread با دریافت یک delegate انتظار دارد که آدرس متد مورد نظر ما جهت انجام وظیفه ای که در Thread جدید قرار است انجام شود را دریافت کند. متد WriteY از نظر Signature (نوع مقدار بازگشتی که در اینجا void و نوع و تعداد پارامترها که در اینجا فاقد پارامتر است) کاملا با متد جایگزین در delegate منطبق است . در ادامه متد Start کلاس Thread فراخوانی شده که فرمان اجرای Thread مذکور را صادر میکند. همزمان و در ادامه، برنامه مشغول انجام وظیفه دیگری (ارسال x به خروجی) می باشد . سیستم عامل در سیستم هایی که تعداد Core پردازنده یکی باشد به طور تقریب هر بار Time Slice یا فرصت زمانی به میزان 20 میلی ثانیه در اختیار هر Thread قرار میدهد و نتیجه همان است که ملاحظه نمودید در سیستم های چند هسته ای (Multicores) وضعیت به گونه نسبتا متفاوتیست که جزییات کیفیت انجام آن، خارج از حوصله بحث ماست.

از آنجاییکه ایجاد، مدیریت و کنترل Thread ها نیاز به تجربه قابل توجهی دارد خوشبختانه یک کامپوننت ویژه که اتفاقا مصداق بارزی برای بحث مدل EAP نیز میباشد برای انجام این کار به نحو بسیار ساده تری پیش بینی شده است. نام این کامپوننت BackgroundWorker میباشد که برنامه نویسان با تجربه حتما از آن بارها در سناریوهای مختلف استفاده نموده اند.
به نظر میرسد در مدل EAP طراحان این مدل به شدت دنبال این هدف بوده اند که برنامه نویسان ضمن بهره گیری از مزایای استفاده از برنامه های Multithreaded ، از بسیاری از جنبه های دشوار و غامض آن دور مانده و از آن جهت ارتقای سطح کیفی در برنامه های خود به راحتی استفاده نمایند.

طراحان این مدل ، استفاده از آن را در موارد زیر توصیه میکنند:

1- اجرای پشت پرده یا در زمینه وظایف زمان بر (Time-consuming tasks) نظیر دانلود و یا عملیات سنگین مرتبط با Database بدون ایجاد وقفه (Interrupt) در وظایف جاری برنامه.

2- اجرای چند عمل همزمان به نحوی که در هنگام اتمام هر یک از آنها به طور جداگانه، سیستم عامل برنامه نویس را با ارائه اطلاعات مفید، از این موضوع آگاه (Notification) نماید.

3- مدیریت اتوماتیک هر Thread به عنوان کوچکترین واحد اجرای یک وظیفه (Task) به نحوی که در صورت کمبود منابع (Resources) (منظور از منابع به طور مشخص حافظه Ram و جلب توجه زمان پردازنده می باشد) به طور موثر و غیر آزار دهنده ای منتظر بمانند.

4- برقراری ارتباط پویا بین عملیات و وظایف (تعیین شده برای اجرای هم زمان) در انتظار (Pending asynchronous operations) با بهره گیری از مکانیسم آشنای Eventها و delegateها.

از نشانه های آشکار کلاسی که بر اساس مدل EAP طراحی شده باشد وجود متد یا متدهایی است که در نامگذاری آنها از الگوی مشترک MethodNameAsync استفاده شده باشد.در برخی از مواقع (و نه در همیشه) این متدها درست نظیر مدل APM دارای معادل Synchronous نیز میباشند که همان عملیات منسب را در Thread جاری برنامه اجرا میکند و بدیهیست که از دیدگاه مقایسه Performance و کارآیی با نظیر خود اصلا قابل مقایسه نیستند. این کلاسها همچنین میتواند شامل Eventهایی با الگوی نام مشترک MethodNameCompleted و MethodNameCancel (یا به سادگی CancelAsync) باشند.

UI و کنترل آشنای PictureBox یکی از مشهورترین کامپوننتهایی است که از مدل EAP حمایت کرده و از آن استفاده میکند. در یک انتخاب ناشیانه، ما قادر هستیم با بهره گیری از متد Load این کامپوننت یک Image خاص را جهت نمایش در این قاب به صورت Synchronous دانلود نماییم. اما اگر حجم تصویر نسبتا زیاد (مثلا بیشتر از 200 کیلو بایت) و یا کیفیت ارتباط شبکه ضعیف باشد، قطعا در خوش بینانه ترین حالت ممکن، کل برنامه به مدت زمان قابل توجه (و البته آزار دهنده ای) متوقف و یا در بدترین حالت به طور کامل Hang خواهد کرد. و در حالت اول این توقف درست تا لحظه اتمام عملیات متد Load ادامه خواهد داشت.

اما انتخاب بهتر برای عدم توقف برنامه و به تعبیری پاسخگو بودن کلیه اجزای رابط کاربر (Responsive UI) در هنگام Loadو یا دانلود تصویر ، فراخوانی LoadAsync و به موازات آن توجه و Handle کردن رویداد LoadCompleted می باشد که به شکل بسیار بسیار محسوسی کیفیت و کارآیی (و به تعبیری ارزش فنی کار) را افزایش خواهد داد.

بعد از فراخوانی LoadAsync برنامه بر خلاف وضعیت قبل بدون هیچگونه توقفی به انجام روال عادی خود ادامه خواهد داد ولی پنهان از دید و نظر برنامه نویس (در Background) یک Thread اختصاصی برای ادامه موثر عملیات Load یا دانلود تصویر آغاز میشود و نیازی نیست که ما نگران جزییات نگران کننده این عمل (مثل زمان انتظار و یا حجم بیش از اندازه تصویر) باشیم. چرا که درست در زمان مناسب یعنی اتمام عملیات ، این خبر مسرت بخش در قالب یک Event به ما اطلاع داده میشود و مهمتر از همه آنکه این Event شامل اطلاعات بسیار ارزشمندی در قالب یک پارامتر از نوع AsyncCompletedEventArgs میباشد که بخشی از آن اطلاعات تصویر Load شده خواهد بود که به سادگی میتوان از آن برای نمایش تصویر در PictureBox استفاده نمود. حتی در صورت موفقیت آمیز نبودن عملیات از همین پارامتر میتوان برای ارائه یک گزارش به کاربر و اعلام نوع و کیفیت خطای ایجاد شده استفاده نمود.

یک وجه دیگر این ماجرا این است که اگر به هر دلیلی در حین انجام عملیات منصرف بشویم امکان ابطال عملیات به نحو موثری با فراخوانی CancelAsync میسر میباشد. یک نکته ظریف در این حالت وجود دارد که لازم است به آن اشاره کنم. به طور طبیعی این امکان وجود دارد که فراخوانی CancelAsync یعنی دستور برای ابطال عملیات، درست همزمان با اتمام موفقیت آمیز این عملیات باشد که این وضعیت ویژه اصطلاحا race Condition نامیده میشود و شرایط و خواص خاص خود را دارد که توضیح جزییات آن باز خارج از حوصله این بحث میباشد.

در انتهای بررسی اجمالی این مدل باید به این موضوع نیز اشاره کنیم که در همین سناریوی Load تصویر، حداقل انتظار یک برنامه نویس با تجربه این است که از زمان تخمینی کل عملیات و مقدار زمان سپری شده یا باقیمانده مطلع باشد تا مثلا با تدارک دیدن یک Progress Bar این اطلاعات به بهترین شکل ممکن در اختیار کاربر قرار بگیرد. متاسفانه این ویژگی نه به صورت اجباری بلکه به صورت اختیاری در این مدل پیش بینی شده است و اتفاقا کامپوننت BackgroundWorker دارای این ویژگی بسیار مفید میباشد. در یک الگوی کلی چنین اطلاعاتی در قالب یک Event با الگوی نام ProgressChanged یا MethodNameProgressChanged تدارک دیده میشود که مطابق معمول اطلاعات مرتبط با پیشرفت عملیات در پارامتری از نوع ProgressChangedEventArgs به داخل Event هدایت میشود. در این وضعیت معمولا ما با بررسی ProgressChangedEventArgs.ProgressPercentage از میزان، مقدار یا درصد کار انجام شده مطلع میشویم که این مقدار در بازه اعداد بین صفر تا 100 قرار دارد. نیازی به توضیح بیشتر نیست که امکان استفاده از این مقدار در یک ProgressBar با تغییر مقدار Value میسر خواهد بود. در صورتیکه مایل باشیم میزان پیشرفت درصد کار انجام شده چندین عمل Asynchronous مختلف را تنها با یک Event کنترل نماییم استفاده از ProgressChangedEventArgs.UserState به عنوان یکی از بهترین راه حل ها توصیه شده است.

به عنوان یک مثال ساده در مدل EAP میتوان به کاربرد ساده ای از کلاس WeClient اشاره کرد.
ابتدا اجزای الگوهای مشترک را در Member های این کلاس ملاحظه کنید:

 

public byte[] DownloadData (Uri address);
public void DownloadDataAsync (Uri address);
public void DownloadDataAsync (Uri address, object userToken);
public event DownloadDataCompletedEventHandler DownloadDataCompleted;
public void CancelAsync (object userState);
public bool IsBusy { get; }

انجام یک دانلود به صورت Asynchronous توسط این کلاس در ساده ترین شکل خود میتواند به صورت زیر باشد:
var wc = new WebClient();
wc.DownloadStringCompleted += (sender, args) =>
{
if (args.Cancelled)
Console.WriteLine ("Canceled");
else if (args.Error != null)
Console.WriteLine ("Exception: " + args.Error.Message);
else
{
Console.WriteLine (args.Result.Length + " chars were downloaded");
}
};
wc.DownloadStringAsync (new Uri ("http://www.SomeWebsite.com"));

 

در خاتمه مایلم اشاره کنم که قطعا برنامه نویسانی که تجربه کار با Silverlight و WCF را دارند، بدون شک بارها از مدل EAP استفاده نموده اند و این مدل تا قبل از ظهور Windows 8 Store Application و مخصوصا در تکنولوژی های مدرن که دغدغه ایجاد UI های Touch Enabled یا قابل لمس را دارند، توصیه شده و در سطح وسیعی استفاده شده است.
اما برای سومین بار، چنانچه در قسمت سوم این مطلب به آن اشاره خواهم کرد، رویکرد و استراتژی Microsoft در ارائه مدلهای برنامه های Asynchronous تغییر کرد و مدل Async-await تنها مدل قابل استفاده در مدرنترین تکنولوژی حال حاضر Microsoft یعنی Windows 8 Store Application میباشد.

 

 مدل Async-await
آخرین مدل توصیه شده Microsoft برای پیاده سازی سناریوهای مرتبط با Asynchronous Programming صرفنظر از پیشرفتهای قابل توجه در ویژگیهای فنی این مدل، یک نمونه عالی از تشریک مساعی CLR با قابلیتهای ارزنده زبان C# به عنوان یک زبان مستقل برای طراحی و تدوین برنامه هایی در کاربردهای عمومی (General Purpose Applications) نیز میباشد.
کلاس Task که در این مدل نقش بسیار مهمی ایفا میکند ابتدا در .Net Framework 4.0 معرفی گردید و بخشی از اهداف بسیار بزرگتری بود که بخشهای وسیعی از این اهداف بعدها در Framework 4.5 محقق گردید.
اگر به لیست تغییرات C# 5.0 نسبت به 4.0 توجه کنیم، ابتدا در ظاهر و در مقایسه با تغیرات قبلی این لیست کمی مأیوس کننده به نظر می رسد. در واقع در صدر این لیست نه چندان مفصل نام مدل Async-await دیده خواهد شد. با مطالعه بیشتر و بررسی دقیق تر این ویژگی، می توان قاطعانه عنوان کرد که تغییرات این نسخه اخیر بیشتر تغییراتی از دیگاه کیفی هستند و نباید کمیت ناچیز لیست این تغییرات، شبهه ای در علاقمندان و برنامه نویسان این زبان قدرتمند ایجاد نماید.
کاربرد کلاس Task در Framework 4.0 صرفا محدود به استفاده در زمینه های Parallel Programming بود. اما در ادامه به طور تخصصی برای برطرف نمودن برخی از نقاط ضعف استفاده از Thread ها طراحی شد که دو نمونه از بارزترین موارد قابل ذکر به شرح زیر میباشند:
1) در کلاس Task دیدگاه Compositional بودن Task بسیار مورد توجه بوده است و معنای دقیق این گفته این است که بر خلاف Thread حالا در این کلاس امکان ارتباط زنجیر وار (Chained) چند Task با استفاده از منطق مشخص امکان پذیر میباشد. بعلاوه در مورد Thread ابدا امکان Restart کردن یک Thread وجود ندارد و پس از استفاده از متد Join که به منزله اجبار به انتظار جهت خاتمه Thread مذکور است، Thread اجرا شده به هیچ وجه امکان درخواست دریافت Time Slice (درخواست برای توجه CPU و اختصاص زمان) را نداشته و برای همیشه خاتمه یافته است.
2) با اینکه در Threadها امکان ورود پارامتر میسر می باشد اما دریافت خروجی از Threadها بسیار دشوار و با استفاده از روشهای عادی امکان پذیر نیست. یکی از روشهای رایج برای انجام این کار استفاده از داده های مشترک (Shared Data) و به طور مشخص فیلدهای Static میباشد که بالقوه امکان بروز خطاهای متعددی را افزایش می دهد. اما کلاس Task برای جبران این نقص تدارک ویژه ای دیده است که در ادامه به اجمال آن را بررسی خواهیم کرد.
در یک توصیف مختصر روند کار با Task از این قرار است که ابتدا در خلال نمونه سازی (Instantiate) این کلاس، عملیات مورد نظر خودمان را (که در ترمینولوژی این مدل، این عملیات به سادگی Job نامیده میشوند) در قالب یک بلاک Action از طریق یکی از 8 سازنده (Constructor) کلاس به کلاس معرفی نموده و متد Start را جهت درخواست کنترل از سوی OS و اختصاص یک زمان بندی مشخص (Specified Scheduling) فراخوانی میکنیم. در یک راهکار جداکانه می توانیم یک نمونه از Object این کلاس را به متدهایی که پارامتری از نوع Task دریافت میکنند منتقل نماییم. که در ادامه مثالهایی در این زمینه ارائه شده است.
این موارد تنها دو نمونه از روشهایی هستند که به طور معمول در سناریوهایی که نیاز به استفاده از Task وجود دارد مورد استفاده قرار میگیرند. واقعیت این است که من از تنوع روشهای موجود در ایجاد Task و روشهای اختصاصی سازی (Customize) نمودن عملیات آن بسیار شگفت زده گشته و البته در برخی از موارد با تناقضهایی هم در نحوه استفاده از آن روبرو شدم که به برخی از مهمترین آنها اشاره خواهم کرد. هر چند متاسفانه تا این لحظه هیچ توضیح منطقی برای آن ندارم.
برای روشن شدن همه این موارد، در ادامه از مثالهای ساده ای استفاده خواهم کرد ، هر چند که من هنوز مخصوصا در Windows 8 Store Application در چندین سناریوی مختلف به دنبال کسب تجربه و تسلط بیشتر برای این مدل جدید میباشم . این تذکر بیشتر به این دلیل است که هیچ ادعایی بر بی نقص بودن این روشها وجود ندارد و اتفاقا انتظار دارم که همکاران محترم حتما در صورت وجود نظرات و یا روشهای بهتر بنده را از این نظریات ارزشمند آگاه نمایند.
مرور توصیف کلاس Task و مخصوصا متد ها و فیلدهای آن که در محدوده نام System.Threading.Tasks و در اسمبلی mscorlib قرار دارد، می تواند ذهنیت بسیار مناسبی را در خوانندگان، از قابلیت های این کلاس ایجاد کند:


public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
{
/* 8 overloaded */
public Task(Action action) { }
/* 21 overloaded */
public ConfiguredTaskAwaitable ConfigureAwait(
bool continueOnCapturedContext) { }
/* 4 overloaded */
public static Task Delay(int millisecondsDelay) { }
public void Dispose() { }
public static Task<TResult> FromResult<TResult>
(TResult result) { }
public TaskAwaiter GetAwaiter() { }
/* 8 overloaded */
public static Task Run(Action action) { }
/* 2 overloaded */
public void RunSynchronously() { }
/* 2 overloaded */
public void Start() { }
/* 5 overloaded */
public void Wait() { }
/* 5 overloaded */
public static void WaitAll(params Task[] tasks) { }
/* 5 overloaded */
public static int WaitAny(params Task[] tasks) { }
/* 4 overloaded */
public static Task WhenAll(IEnumerable<Task> tasks) { }
/* 4 overloaded */
public static Task<Task> WhenAny(IEnumerable<Task> tasks) { }
public static YieldAwaitable Yield() { }
public object AsyncState { }
public TaskCreationOptions CreationOptions { }
public static int? CurrentId { }
public AggregateException Exception { }
public static TaskFactory Factory { }
public int Id { }
public bool IsCanceled { }
public bool IsCompleted { }
public bool IsFaulted { }
public TaskStatus Status { }
}


ساده ترین شکل استفاده از Task ، بدون نیاز به نمونه سازی (Instantiate) فراخوانی متد استاتیک Run و استفاده از delegate و یا بهره گیری از تکنیک Lambda مییاشد که همانگونه که همکاران محترم مستحضر می باشند این دو تکنیک اغلب هر کدام به عنوان یک روش جایگزین (Alternative) در بسیاری از کاربردها به جای هم مورد استفاده قرار میگیرند . در مورد مزیت و یا ضعف های هر یک از این دو روش نسبت به یکدیگر باید اطمینان داشت که اساسا هر دو روش از دیدگاه Performance، کاملا یکسان بوده و معیار انتخاب برنامه نویسان در این مورد بیشتر وضوح و خوانایی کد و کاملا وابسته به سلیقه شخصی است.
به این جهت با کسب اجازه از محضر شما بزرگواران من هر دو روش را در دو مثال جداگانه و به شرح زیر ذکر خواهم نمود.

متد استاتیک Run خود دارای 8 نسخه Overload  شده می باشد
روش اول استفاده از تکنیک Lambda


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AsyncUsingTask
{
class Program
{
static void Main()
{
Task displayTask = Task.Run(() => Console.WriteLine("async in C# 5.0")); /* ادامه تا زمانیکه عملیات منتسب خاتمه پیدا کند*/
while (true)
{
/* بررسی شرط خاتمه عملیات */
if (displayTask2.IsCompleted)
{
Console.WriteLine("Task completed!");
break;
}
}
Console.ReadLine();
Console.WriteLine("Press <Enter> to Exit.");
}
}
}


روش دوماستفاده از تکنیک delegate


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AsyncUsingTask
{
class Program
{
static void Main()
{
Task displayTask = Task.Run(delegate()
{
Console.WriteLine("async in C# 5.0 delegate");
});
/* ادامه تا زمانیکه عملیات منتسب خاتمه پیدا کند*/
while (true)
{
/* بررسی شرط خاتمه عملیات */
if (displayTask2.IsCompleted)
{
Console.WriteLine("Task completed!");
break;
}
}
Console.ReadLine();
Console.WriteLine("Press <Enter> to Exit.");
}

}
}

 

و در نهایت خروجی برنامه عبارت است از:

 

async in C# 5.0
Task completed!
Press <Enter> to exit.

 

چنانچه قبلا نیز اشاره شد و قابل پیش بینی نیز هست با استفاده از سازنده کلاس Task نیز امکان ایجاد یک Task وجود دارد.


using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace AsyncUsingTask
{
class Program
{
static void Main()
{
Task displayTask = new Task(() => Console.WriteLine("async in C# 5.0"));
displayTask.Start();
displayTask.Wait();
Console.ReadLine();
}
}
}

 

یکی از موارد تناقضی که قبلا از آن یاد کردم در بحث استفاده از سایر فرم های Overload شده سازنده میباشد. در حقیقت بر اساس مستندات موجود در کلاس Task این انتظار وجود دارد که یکی از سازنده ها دو پارامتر ی:ی از نوع Action و دیگری از نوع یک Enumeration به نام TaskCreationOption دریافت نماید که متاسفانه هنگام استفاده از آ« من با خطای Syntax در Visual Studio 2012 روبرو شدم و در حال حاضر هم هیچ توضیحی برای آن ندارم.
اما همانطور که ملاحظه میکنید متد Start هم که بر خلاف Run یک Instance Method می باشد نیز قادر به اجرای یک نمونه از قبل ایجاد شده از کلاس Task میباشد. تفاوت اصلی و بسیار قابل توجه متد Start مثلا با متد Run صرفنظر از تفاوت نوع تعلق به کلاس، در این است که با استفاده از این متد در حقیقت Task مذکور با استفاده از کلاس TaskScheduler در یک جدول زمان بندی قابل توجه با انتخابهای متعدد قرار میگیرد که در ادامه آن را بررسی خواهیم کرد اما قبل از آن من مایل هستم که دو مورد از ویژگیهای برجسته کلاس Task را که قبلا به یکی از آنها اشاره کردم، در قالب یک مثال ساده بیان کنم. ابتدا لطفا به کد مثال توجه نمایید:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace AsyncUsingTask
{
class Program
{
static void Main()
{
Task<string> displayTask = new Task<string>(() => "async in C# 5.0");
displayTask.Start();
Console.WriteLine("The result from the Task {0}", displayTask.Result);
Console.ReadLine();
}
}
}


اولین نکته قابل ذکر در این مثال استفاده از تکنیک Generic در کلاس Task میباشد که لازم به ذکر نیست که همین ویژگی به تنهایی قابلیت Flexiblily و انعطاف بسیار مهمی را به این کلاس اضافه نموده است. نکته دوم قابلیت استفاده از مقادیر بازگشتی آن هم به ساده ترین روش ممکن بعد از اجرای وظیفه یا Job منتسب به Task می باشد که آن را نخصوصا نسبت به Thread متمایز میسازد.
به هر حال بدون تردید در هنگام استفاده از کلاس Task و مخصوصا با توجه به وضعیت های متعددی که در هنگام کار با این کلاس ممکن است با آن روبرو بشویم، نیاز مبرمی به وجود ی: مکانیسم مشخص جهت آگاهی از ای« وضعیت ها ضروری به نظر میرسد.
برنامه نویسی که کاربرد مشخصی از کلاس Task را در یک سناریوی دلخواه پیاده سازی میکند نیاز دارد تا حداقل مطمئن باشد که آیا Task آغاز شده خاتمه یافته، احیانا Cancel شده و یا احتمالا با خطایی روبرو شده است. خبر خوب در این زمینه این است که یک Enumeration ویژه به نام TaskStatus اساسا برای پیگیری و اطلاع از همین موضوع پیش بینی شده است که به صورت زیر در System.Threading.Tasks تعریف شده است:


public enum TaskStatus
{
Canceled = 6,
Created = 0,
Faulted = 7,
RanToCompletion = 5,
Running = 3,
WaitingForActivation = 1,
WaitingForChildrenToComplete = 4,
WaitingToRun = 2
}


برای اطلاع از این وضعیتها فقط کافیست که مقدار Status را که یک Data Member با دسترسی public است بررسی نماییم. مقدار Created توسط CLR و درست در هنگام نمونه سازی کلاس Task (و یا چنانچه پیشتر ملاحظه کردیم توسط متد استاتیک Run) در Status قرار میگیرد. در ادامه درست همزمان با شروع Task مقدار Status به WaitingToRun تنظیم خواهد شد. اجرای Task با مقدار ناچیزی تاخیر آغاز میشود که نحوه کنترل آن و چگونگی انجام آن در ادامه و در مبحث TaskSceduler بیان خواهد شد.
اما به هر حال بعد از اجرا مقدار Status به Running تنظیم شده و در صورت توقف احتمالی Task مقدار Status به WaitingForChildrenToComplete تنظیم میگردد. در ادامه CLR مقدار Status را بعد از خاتمه یک Task یه یکی از سه مقدار RunToCompletion یا Canceled یا Faulted تغییر خواهد داد که تصور میکنم خود مقادیر به اندازه کافی گویا هستند .
مقدار RunToCompleted در Status خاتمه موفقیت آمیز یک Task را به ما اطلاع میدهد و Canceled بیان کننده این مطلب است که اجرای Task مذکور به دلایلی (که در جای خود حتما به آنها خواهیم پرداخت) بدون حصول به نتیجه، خاتمه یافته است. و مقدار Faulted حاوی اخبار مایوس کننده ای مبنی بر بروز یک خطا در حین اجرای وظایف منتسب به Task ، می باشد.
نظر به اهمیت این سه وضعیت این کلاس دارای سه Accessor با دسترسی public متناظر با این سه وضعیت میباشد که برای اطلاع از هر یک از این سه وضعیت می توان به جای بررسی Status از آنها استفاده نمود.
یکی دیگر از قابلیت های بسیار مهم در کلاس Task اصطلاحا Continuations نام دارد که قبل از توضیح آن لازم است به یک مثال ساده اما مهم در این باره توجه نمایید:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace AsyncUsingTask
{
class Program
{

static void Main(string[] args)
{
ShowEvenNumbers();
Console.WriteLine("Waiting.....");
for (int i = 0; i <= Int16.MaxValue * 100; ++i) ;
Console.WriteLine("End.");
Console.ReadLine();
}
static void ShowEvenNumbers()
{
Func<int> MFunc = MyMethod;
//Task<int> evenNumbersTask = Task.Run(() => Enumerable.Range(1,50000).Count(item => item%2==0));
Task<int> evenNumbersTask = Task.Run(MFunc);
TaskAwaiter<int> awaiterEven = evenNumbersTask.GetAwaiter();
awaiterEven.OnCompleted(() =>
{
Console.WriteLine("Complete, Total no of even : {0}",
awaiterEven.GetResult());
});
Console.WriteLine("Schedule to complete...");
}
static int MyMethod()
{
return Enumerable.Range(1, 50000).Count(item => item % 2 == 0);
}
}
}

 

 در ترمینولوژی Asynchronous Programming اصطلاح Continuation code block اشاره به بلاک مشخصی از دستورات است که بلافاصله پس از اتمام عملیات، اجرا می گردد.
در مدل Async-await این کار با استفاده از سه تکنیک به شرح زیر قابل انجام است:


1- استفاده از متد OnCompleted
2- استفاده از متد ContinueWith
3- استفاده از Keyword یا دستور await


آخرین مثالی که در پایان قسمت سوم ذکر شده بود، یک پیاده سازی ساده از روش اول میباشد که در متد ShowEvenNumbers و در سطر اول این متد ، هدف فقط یادآوری این نکته بوده است که امکان استفاده از تکنیک delegateهای Generic میسر است. متد استاتیک MyMethod دقیقا چنین نقشی را عهده دار بوده و در این مثال ساده تعداد اعداد زوج بین 1 تا 50000 محاسبه شده است.
در سطر دوم متد ShowEvenNumbert دقیقا همین عمل با استفاده از تکنیک Lambda انجام شده و البته Comment شده است.
از دیدگاه تحلیل کد، متد استاتیک Range متعلق به کلاس Enuerable به سادگی یک Colletion (غیر Generic) از محدوده اعداد بین 1 تا 50000 ایجاد میکند. ناگفته پیداست که در این هنگام و در سناریو های مورد نیاز به سادگی میتوان این Collection را به یک List از نوع Generic تبدیل نمود(مثلا با استفاده از متد ToList یا انتقال خروجی Range مستقیما به سازنده یک لیست Generic نظیر List<int> ).
با علم بر Collection بودن خروجی متد Range بنابراین امکان استفاده از متد Count که انتظار دریافت یک Predict را دارد، خیلی دور از ذهن نیست.
اما در سطر سوم متد ShowEvenNumber این Task متفاوت ما مجددا این بار با استفاده از متد استاتیک Run ایجاد شده است.
تاکید من بر کلمه متفاوت از آن جهت است که بوضوح ردپای قابلیت Generic کلاس Task را مشاهده میکنیم و همین ویژگی (منظور قابلیت ورود یک نوع یا Type به داخل Task و انتظار دریافت خروجی) آن را نسبت به سایر روشهای Asynchronous Programming که تا به حال بررسی کردیم متمایز میسازد.
اما در ادامه برای دستیابی به یک Continuation code block که امکان اجرای بلاک مشخصی از کد را در پایان عملیات Task امکان پذیر نماید نیاز به یک TaskAwaiter وجود دارد که مطابق مثال، به سادگی با فراخوانی متد GetAwaiter ، این نیاز برطرف خواهد شد.
به نظرم توضیح این نکته در اینجا ضروریست که TaskAwaiter در واقع یک Struct با قابلیتهای Generic است (این نکته از آن جهت حائز اهمیت است که به طور معمول ما باید استراتژی های جداگانه، مشخص و تعیین شده ای در هنگام کار با یک Value Type و یا Reference Type داشته باشیم) و در System.Runtime.CompilerServices تعریف شده و جالب است بدانیم که به دلیل وظیفه بسیار حساسی که به عهده دارد اینترفیس های ICriticalNotifyCompletion و INotifyComletion را Implement نموده است:


public struct TaskAwaiter<TResult> :
ICriticalNotifyCompletion, INotifyCompletion
{
public bool IsCompleted {}
public void OnCompleted(Action continuation) {}
public void UnsafeOnCompleted(Action continuation) {}
public TResult GetResult() {}
}

 

اینک نوبتی هم که باشد نوبت مشخص نمودن بلاک کد مورد نظر است که انتظار داریم در داخل آن و درست در لحظه انتهای عملیات Task، نتیجه عملیات را دریافت کنیم.

این کار با تکنیک Lambda و استفاده از متد OnCompleted انجام شده و در داخل بلاک تعیین شده یک فراخوانی ساده متد GetResult چنانچه از نام آن نیز مشخص می باشد، حاوی نتیجه عملیات Task مورد نظر ما خواهد بود.
در یک جمع بندی در این مثال سه نکته مورد توجه بوده است . اول امکان Generic بودن Taskها . دوم امکان تعیین یک Continuation code block برای دریافت خروجی عملیات منتسب به Task و در نهایت سوم امکان دریافت خروجی از طریق متد GetResult.

چنانچه در ابتدای همین قسمت اشاره شد سه روش برای دستیابی به Continuation code block وجود دارد که من جهت پرهیز از اطاله کلام فقط به اشاره کوتاهی به روش دوم یعنی استفاده از متد ContinueWith بسنده خواهم نمود. بنابراین فقط با یک مثال کوتاه به استفاده از آن در متد ShowEvenNumbers خود اشاره کرده و تفاوت آن را ذکر خواهم کرد:


evenNumbersTask.ContinueWith(task =>
{
Console.WriteLine("Complete, Total no of even : {0}",
task.Result);
});

 

این متد دارای 19 فرم Overload شده است!!! که در این مثال صرفا با دریافت یک پارامتر delegate (Func<TResult>) و استفاده از Property یا خاصیتی به نام Result در همین پارامتر، امکان گزارش خروجی عملیات Task را فراهم می نماید.
اما تا اینجا همه انرژی ما در تشریح این مدل صرف بررسی اجمالی Task شد و هنوز ردپایی از async-awiat مخصوصا به ترتیبی که در مستندات C# 5.0 آمده است وجود ندارد.
در یک جمع بندی مختصر، آنچه تا این لحظه بررسی شد محوریت نقش کلاس Task و استفاده از یک Continuation code block برای دریافت خروجی بود.
اما خبر خوب در اینجا این است که اساسا هدف از async-wait ایجاد روال ها ویا متدهای Asynchronous بدون نیاز به تعیین یک Continuation code block جداگانه میباشد. آنچه مصرانه توسط Microsoft در هنگام معرفی این مدلها توصیه شده است ، لزوم استفاده در سناریوهاییست که نیاز به برنامه های I/O-bound یا Compute-bound وجود دارد.
اصطلاح I/O-bound به عملیاتی اطلاق میشود که بیشتر زمان خود را صرف انتظار (waiting) برای انجام کاری مشخص میکنند. از نمونه های بارز آن میتوان به دانلود یک Web Page و یا حتی انتظار ناشی از فراخوانی ReadLine اشاره کرد. به عبارت ساده تر توقف ناشی از انتظار دریافت از ورودی و یا ارسال به خروجی در محدوده عملیات I/O-bound می باشند. در مقابل Compute-bound که گاهی CPU-bound نیز نامیده میشود، به عملیاتی اطلاق میگردد که انتظار، صرفا جهت درگیری پردازنده برای محاسبات دشوار و زمان بر میباشد. به عنوان مثال میتوان به مرتب سازی و Sort تعداد بیشماریاز رکوردها اشاره نمود.
توجه به این ویژگی منحصر به فرد که وظیفه ایجاد Continuation code block در هنگام استفاده از کلیدواژه های (از به کار بردن این معادل برای Keyword واقعا متنفرم اما چاره ای نیست) async و await به عهده خود کامپایلر میباشد ما را ملزم میکند که اگر موافق باشید زمان هر چند کوتاهی را به بررسی اجمالی روش استفاده از آن اختصاص بدهیم.
در ابتدا برای استفاده از این Keyword ها توجه به سه شرط اساسی ، اجتناب ناپذیر است:
1- متد مشخص شده با asunc حتما باید دارای یک خروجی از نوع Task یا Task<T> و حتی حداقل void باشد
2- متد مورد نظر ما که برای انجام عملیات Asynchronous انتخاب میشود حتما باید در ابتدای متد صریحا Keyword جدید async را به عنوان یک Modifier ذکر نماید. وجود آن به تنهایی تضمین کننده انجام عملیات به صورت Async نیست اما در صورت عدم ذکر آن کامپایلر، قطعا متد ما را در حالت Synchronous اجرا خواهد کرد
3- این عملیات همیشه و در همه حال Discontinuous و منقطع می باشد و چنانچه در مثال خواهیم دید ذکر دستور await اجرای عملیات را به مسیرهای متفاوت و بعضا نا آشنایی مثل وضعیت تعلیق (Suspension) هدایت میکند که این لزوما خبر بدی نیست اما نیاز به مراقبت و کنترل های خاص دارد که به بیراهه کشیده نشود.

 

class Program
{
static void Main()
{
Task showNumber = ShowEvenNumbers();
Console.ReadLine();
}
public static async Task ShowEvenNumbers()
{
await Task.Run(() => Console.WriteLine("Async Function"));
}
}

 

من در این مثال عمدا سایر جزییات غیر ضروری را حذف نموده و فقط در خلاصه ترین شکل ممکن نمونه سادهای از نحوه استفاده از Syntax و الگوی اصلی را مشخص نموده ام.
توجه به نقش محوری کلاس Task و وجود و نحوه استفاده از Keywordهای async و await در این مثال اهمیت زیادی دارد.
اینک من برای پرهیز از عذاب وجدان لازم میدانم که با چند تغییر مختصر آن را با برخی از دانسته های خود که در بحث های قبلی به آنها اشاره کردیم ترکیب کرده و نتیجه را در مثال دیگری نشان بدهم . قطعا تفسیر و تحلیل این کد دیگر نیازی به توضیح اضافی نخواهد داشت.

 

class Program
{
static void Main()
{
Task showNumber = ShowEvenNumbers();
Console.ReadLine();
}
public static async Task<int> ShowEvenNumbers()
{
return await Task.Run(() =>
{
Console.WriteLine("Async Function");
});
}
}

 

من در قسمت اول این رشته از مطالب اشاره کردم که اساسا انگیزه اصلی من در بررسی اجمالی سیر تحول روشهای Asynchronous Programming در .NET تاکید بسیار زیاد Microsoft در قسمت وسیعی از کدهای Platform اختصاصی و جدید آن به نام Windows 8 App Store بود که صرفا برای طراحی و پیاده سازی برنامه های Metro Style قابل اجرا در Windows 8 تاکید بسیار زیادی بر روی آن وجود دارد. موضوع عدم انتظار UI در این Platform و با در نظر گرفتن معماری Metro Style به صورت بسیار جدی در لایه های محافظتی خود OS و با تکنیکهای بسیار پیچیده ای نظیر تعلیق و تجدید نظر در معماری برنامه ها از اهمیت بسیار بالایی برخوردار است.
نظر شخصی من در مورد این Platform جدید مایکروسافت این است که اولا آینده بسیار درخشانی خواهد داشت و ثانیا تعمیم ایده بسیار موفق ارائه برنامه ها بدون واسطه در فروشگاههای مجازی نرم افزار (که تجربیات موفق Google و Apple را به همراه دارد) روشهای نوینی در تکنولوژی های تولید برنامه Microsoft معرفی میکند. به همین دلیل در خاتمه با ذکر یک مثال ساده در این Platform مجموعه مباحث مرتبط با این موضوع را به پایان خواهم رساند.
در این مثال هدف Serialize و DeSerialize یا به تعبیری نوشتن و خواندن یک Dataی ساده در Platform جدید مایکروسافت است که در مورد دستیابی به منابع سیستم مثل دیسک و حافظه ذخیره سازی مخصوص و ایزوله شده ای به نام Isolation Storage Area استراتژی های خاص و بسیار متفاوتی نسبت به گذشته دارد و در این مورد سختگیریهایی زیادی انجام میدهد.
شاید عجیب تر ین خبر درباره این Platform این باشد که هیچ اثری از System.Data.SqlClient نیست و معنی دقیق این گفته این است که امکان دسترسی با روشهای مستقیم به Microsoft Sql Server به هیچ وجه وجود ندارد مگر با استفاده از WCF و آن هم با تدابیر بسیار متفاوت.
این کد مستقیما از کتاب Real World Windows 8 Development اثر Samidip Basu از انتشارات APress درج شده است. به نظرم عملیات اصلی کد تا اندازه بسیار زیادی گویا بوده و بنابراین از توضیحات اضافی در این باره پرهیز میکنم.

 

private async void SaveCustomData()
{
MemoryStream customDataToSave = new MemoryStream();
DataContractSerializer serializer = new
DataContractSerializer(typeof(ObservableCollection<ApressBook>));
serializer.WriteObject(customDataToSave, FeaturedBookListVM.FeaturedApressBooks);
// Write serialized custom data to File on HardDisk.
StorageFile fileToWrite = await
localFolder.CreateFileAsync("CustomSerializedFile.xml", CreationCollisionOption.ReplaceExisting);
using (Stream fileStream = await fileToWrite.OpenStreamForWriteAsync())
{
customDataToSave.Seek(0, SeekOrigin.Begin);
await customDataToSave.CopyToAsync(fileStream);
await fileStream.FlushAsync();
}
}
private async void ReadCustomData()
{
StorageFile fileToRead = await localFolder.GetFileAsync("CustomSerializedFile.xml");
using (IInputStream inStream = await fileToRead.OpenSequentialReadAsync())
{
// Read data from File and Deserialize.
DataContractSerializer serializer = new
DataContractSerializer(typeof(ObservableCollection<ApressBook>));
FeaturedBookListVM.FeaturedApressBooks =
(ObservableCollection<ApressBook>)serializer.ReadObject(inStream.AsStreamForRead());
}
}

 

۱ نظر موافقین ۰ مخالفین ۰ ۳۰ مهر ۹۳ ، ۲۰:۵۸
مهران حسین نیا

 چنانچه میدانیم، یک Pointer تنها فقط آدرس قسمتی از حافظه است که در C و C++ از آن برای دستیابی به مقدار ذخیره شده در آن ناحیه از حافظه که آدرس آن توسط Pointer تامین میگردد ، استفاده میکنیم.
اما این آدرس همیشه اشاره گری به یک مقدار (Value) نیست و گاهی بر اساس نیاز ممکن است اشاره گری به یک متد یا Function باشد.
در حالت اول برای ساده تر بیان کردن مفهوم فوق میتوان گفت که اگر آدرس مقدار موجود در ناحیه ای از حافظه با نام P در متغیری از نوع Pointer به نام V ذخیره شده باشد، آنگاه V اشاره گری به P نامیده میشود.
چنانچه گفته شد این مقدار میتواند آدرس یک Function یا یک متد باشد که در این حالت Function Pointer یا اشاره گری به یک تابع نامیده میشود و کاربردهای بسیار متنوع و جالبی دارد. در این مطلب این موضوع ابتدا با یک مثال ساده بیان شده و در قسمتهای بعد تاکید اصلی من اشاره و بحث درباره این واقعیت است که مفهوم Function Pointer دقیقا در C# به عنوان منبع الهام مفهوم Delegates و حتی Events مورد استفاده قرار گرفته شده و در نهایت با استفاده از مفهوم Generics به تکامل رسیده و گسترش یافته است.
این موضوع در اغلب کتابهای مرجع مطرح میشود اما معمولا به سادگی از آن عبور میکنند.

مشخصا یک Function Pointer، حاوی آدرس تابعیست که Signature آن (منظور تعداد و نوع پارامترهای ورودی و همچنین نوع مقدار بازگشتی) دقیقا در زمان تعریف مشخص میگردد.
برای یادآوری و توضیح دقیق تر این مفهوم مثال ساده زیر در C می تواند کمک موثری باشد:

 

‪#‎include‬ “stdafx.h”
int addition(int a)
{
Return a+10;
}
Int pointerTest(int* a)
{
*a=100;
Return *a;
}
int main(int argc, _TCHAR* argv[])
{
int aVariable =10;
printf("%d\n", pointerTest(&aVariable));
int (*functionPointerInC)(int);
functionPointerInC = &addition;
printf("%d",(*functionPointerInC)(1) );
return 0;
}


بدیهیست که این برنامه خروجی زیر را ایجاد خواهد کرد:
100
11

در مثال فوق به جز تابع اصلی main دو تابع مختلف وجود دارند که اولی با نام addition یک پارامتر ورودی نوع int دریافت نموده و یک خروجی باز از نوع int عودت می دهد. دومین تابع با نام pointerTest که یک پارامتر ورودی از نوع اشاره گری به int دریافت نموده و مقدار بازگشتی آن نیز از نوع int می باشد.
در داخل main ابتدا متغیری به نام aVariable از نوع int تعریف و همزمان به مقدار مشخصی، مقدار دهی (Initialize) میگردد. در ادامه تابع pointerTest فراخوانی شده و آدرس متغیر aVariable به عنوان پارامتر ورودی وارد این تابع میشود. بر اساس جزییاتی که در تابع pointerTest مشاهده میکنیم، مقدار بازگشتی این تابع مقدار 100 خواهد بود.در سطر سوم تابع main، یک Function Pointer به نام (Identifier) نمادین functionPointerInC تعریف کرده ایم که بر اساس آنچه از Signature (تعداد و نوع پارامترها و نوع مقدار بازگشتی) این تابع مشخص است این اشاره گر میتواند شامل یا حاوی آدرس هر تابعی باشد که Signature آن با چیزی که در هنگام تعریف این اشاره گر مشخص شده، (یعنی یک پارامتر از نوع int و مقدار بازگشتی از نوع int) مطابقت داشته باشد.
اگر به تابع addition با دقت بیشتری نگاه کنیم، متوجه میشویم که این تابع دقیقا دارای چنین ویژگیهاییست. . همین امر سبب شده است که ما در ادامه آدرس این تابع را در متغیر functionPointerInC ذخیره کنیم.
اینک که functionPointerInC حاوی آدرس تابع addition میباشد، ما به سادگی قادر خواهیم بود که از طریق این متغیر اشاره گر ، تابع addition را فراخوانی نماییم. از آنجاییکه تابع addition نیازمند دریافت یک مقدار از نوع int به عنوان پارامتر ورودی میباشد ما آن را تامین نموده و توسط functionPointerInC آن را احضار نموده ایم .

...اما چنانچه قبلا نیز اشاره شد، طراحان C# مفهوم Function Pointer را هوشمندانه گسترش داده و آن را با ویژگیهای این زبان Object Oriented سازگار نموده اند. همین موضوع، یعنی استفاده از آن در یک زبان Object Oriented ، ما را ملزم میکند که در مورد Static Methods یا Instance Method تمهیدات جداگانه ای در نظر بگیریم. در C# نوع Delegate به طور غیر مستقیم از System.Delegate و یا در صورتیکه Delegate از نوع Multicast باشد از نوع System.MulticastDelegate منشعب (Derived) شده است.

نکته

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

برای آشنایی بهتر با Delegate و درک صحیحی از تفاوت بین Delegate و MulticastDelegate هیچ چیز بهتر از بیان یک مثال ساده نیست.

 

namespace DelegatesInCSharp
{
class Program
{
delegate int DelegateOfTheCalculator(int a, int b);
static void Main(string[] args)
{
DelegateHandler();
}
static void DelegateHandler()
{
StandardCalculator standardCalculator = new StandardCalculator();
DelegateOfTheCalculator delegateOfTheCalculator =
new DelegateOfTheCalculator(standardCalculator.Add);
Console.WriteLine("Sum of a and b is:{0}", delegateOfTheCalculator(80, 10));
Console.WriteLine("Please press <Enter> to exit...");
Console.ReadLine();
}
}
public class StandardCalculator
{
public int Add(int a, int b) { return a + b; }
}
}


در ابتدای کلاس Program یک Delegate با نام DelegateOfTheCalculator معرفی شده است که به روشنی Syntax دقیق متدی را که DelegateOfTheCalculator قرار است نمایندگی آن را به عهدا داشته باشد، مشخص نموده است.
در مثال ما متد Add که در واقع یک Instance Method متعلق به کلاس StandardCalculator میباشد دقیقا دارای شرایطی است که Delegate آن را تعیین نموده و انتظار آن را دارد.
از سوی دیگر یک متد static به نام DelegateHandler در کلاس Program وجود دارد که نمونه سازی کلاس StandardCalculator و در ادامه فراخوانی و یا به طور دقیق تر Invoke نمودن متد Add در همینجا انجام شده است.
در مهمترین قسمت متد DelegateHnadler چنانچه اشاره شد Invoke یا فراخوانی متد Add به طور غیر مستقیم توسط Delegate مورد نظر ما یعنی delegateOfTheCalculator انجام شده و چنانچه این متد انتظار دارد دو عدد از نوع int به ترتیب 80 و 10 به عنوان پارامترهای ورودی، وارد این متد شده و خروجی محاسبه شده یعنی عدد 90 توسط Writeline در خروجی ظاهر میشود.
همانطور که قبلا نیز اشاره کردیم مفهوم Delegate بسیار گسترده تر از Function Pointer بوده و مثال بالا نیز به روشنی نشان میدهد که استفاده از آن نیز بسیار راحت تر شده است. برای نمایش برخی از این تواناییها ما نیز مثال قبلی خود را با کمی تغییر گسترده تر نموده و در آن Multicast Delegate را بررسی میکنیم.

 

namespace DelegatesInCSharp
{
class Program
{
delegate int DelegateOfTheCalculator(int a, int b);
static void Main(string[] args)
{
DelegateHandler();
}
static void DelegateHandler()
{
StandardCalculator standardCalculator = new StandardCalculator();
DelegateOfTheCalculator delegateOfTheCalculator =
new DelegateOfTheCalculator(standardCalculator.Add);
delegateOfTheCalculator += standardCalculator.Sub;
delegateOfTheCalculator += standardCalculator.Mul;
delegateOfTheCalculator += standardCalculator.Div;
//delegateOfTheCalculator -= standardCalculator.Sub;
var addressOfAddMethod = typeof(StandardCalculator).GetMethod("Add").MethodHandle.
GetFunctionPointer();
var methodParams = new object[] { 80, 10 };
System.Reflection.MethodInfo miAdd = typeof(StandardCalculator).GetMethod("Add");
System.Reflection.MethodInfo miSub = typeof(StandardCalculator).GetMethod("Sub");
Console.WriteLine("Sum of a and b is:{0}", miAdd.Invoke(standardCalculator, methodParams));
Console.WriteLine("Result of subtracting b from a is:{0}", miSub.Invoke(standardCalculator, methodParams));
Console.WriteLine("Please press <Enter> to exit...");
Console.ReadLine();
}
}
public class StandardCalculator
{
public int Add(int a, int b) { return a + b; }
public int Sub(int a, int b) { return a > b ? a - b : 0; }
public int Mul(int a, int b) { return a * b; }
public int Div(int a, int b) { return ((a > b) && (b!=0)) ? a / b : 0; }
}
}

 

تفاوتها در اینجا بسیار آشکار میباشند. اولا لازم است به استفاده از اپراتور های Overload شده =+ و =- توجه کنیم. البته من برای آنکه فقط استفاده از اپراتور =- را یادآوری کنم، آن را ذکر کرده ولی آن را Comment کرده ام. در یک نگاه کلی تفاوت بارز در اینجاست که حالا delegateOfTheCalculator بر خلاف مثال قبلی که نمایندگی یک متد را به عهده داشت ، اینک به تعبیری اشاره گری به هر 4 متد کلاس StandardCalculator است . البته اگر بخواهیم به همان روش قبلی متد مورد نظر خودمان را فراخوانی کنیم، متوجه خواهیم شد که تنها امکان Invoke یا فراخوانی آخرین متدی که با استفاده از اپراتور =+ اضافه شده (یعنی متد Div) فراهم خواهد شد. پس برای غلبه بر این مشکل با کمی کمک از Reflection و بهره گیری از MethodInfo به ترتیبی که در مثال ملاحظه میکنیم قادر هستیم هر یک از چهار متد مورد نظر را در مواقع نیاز فراخوانی کنیم. همچنین لازم به ذکر است که من از متغیر Dtynamicی به نام addressOfAddMethod استفاده کرده ام و هدف من از ذکر آن در متن مثال، صرفا نشان دادن روشی برای بررسی برخی جزییاتی است که ممکن است بعضی از دوستان علاقمند بتوانند آدرس دقیق متدهای مورد نظر را در سیستم خود بررسی و مشاهده نمایند.
همچنین اولین پارامتر متد Invoke در این مثال لزوما یک Object از کلاس StandardCalculator میباشد و اگر متدهای این کلاس از نوع static در نظر گرفته شوند بودن یا نبودن مقدار مناسب در پارامتر اول هیچ تاثیری نداشته و حتی میتوان از مقدار null برای این پارامتر استفاده نمود.
با اشتیاق منتظر شنیدن نظرات سروران و دوستان محترم هستم. امیدوارم همه دوستان اشتباهات و کاستی های احتمالی را که مستقیما ناشی از بضاعت اندک من می باشد بخشیده و آن را گوشزد نمایند.

۰ نظر موافقین ۰ مخالفین ۰ ۳۰ مهر ۹۳ ، ۲۰:۳۷
مهران حسین نیا

 در اوایل دهه 90 میلادی و پس از انتشار تئوری وب توسط Tim Berners-Lee که در فاصله بسیار کوتاهی با کمک Robert Cailliau در قالب پروژه World Wide Web به مرحله اجرا رسید، طولی نکشید که سطح توقع و انتظار از وب تا حد تبدیل به یک Platform جهت اجرای Application ارتقا پیدا کرد و ماهیت Stateless بودن این Platform لاجرم مستلزم معماری ویژه ای بود که در مقایسه با برنامه های Desktop تفاوت های برجسته ای داشت. به طور دقیق از سال 1995 رسما مفهوم Web-based Applications مصطلح گردید.
هدف من از ببان اجمالی این تاریخچه کوتاه ، مروری بر این واقعیت می باشد که مفهوم Design Patterns مدتها قبل از ظهور وب و به دلیل احساس نیاز شدیدی که مبنی بر Modular کردن و تخصصی نمودن وظایف کد و امکان استفاده از مفهومی نظیر Encapsulation و بسته بندی قطعات کد به نحوی که به طور مستقل در سایر سناریوها نیز به کار گرفته شود، وجود داشت.
دغدغه جدا سازی وظایف اصلی یک Application باعث شده تا مدلهای متعددی برای دستیابی به هدف فوق ایجاد و منتشر شود که مدلهای n-Layers یا چند لایه ، مدل و معماری MMVM ، MVC و ... ار جمله این موارد میباشند.
در فاصله زمانی که اشاره کردم مخصوصا در ارتباط با وب و ظهور اولین web server ها و مرورگرها مجموعه ای از رویدادها و دستاوردهای شگفت انگیزی وجود دارد که ذکر جزییات آن در این فرصت مقدور نیست. در صورت تمایل به دریافت اطلاعات بیشتر میتوان به اولین وب سایت جهان به آدرس
http://info.cern.ch/
و یا
http://public.web.cern.ch/
مراجعه نمود. نکته قابل توجه و جالب اینکه این سایت بعد از گذشت بیشتر از 20 سال هنوز اصالت و طراحی اولیه خودش را حفظ کرده است و یک تاریخچه مفید از مراحل رشد و ترقی این پروژه تاریخی را ارائه می کند.
اما با توجه به برخی ابهامات و سوالهای مطرح شده درباره معماری MVVM و یا یکی از مشتقات آن یعنی MVP یا MVC شاید مرور تاریخچه ای اجمالی از مراحل تدوین آنها خالی از لطف نباشد.
اگر برای بررسی مفهوم Design Pattern، ریشه آن و تلاشهای انجام شده مرتبط با آن به منابع معتبر مراجعه کنیم، بی تردید با نام دکتر Trygve Reenskaug روبرو خواهیم شد.
ایشان یکی از نوابغ موسسه مشهور Xerox PARC میباشند که در سال 1973 به عنوان سرپرست و یکی از اعضای تیم تحقیقاتی SmallTalk برای تدوین راهکارها و مفاهیمی برای استقلال بخشهای متفاوت کد، بر اساس وظیفه ای که برای کد در نظر گرفته شده است، فعالیت های بسیار قابل توجهی داشته است این تلاشهای مستمر، منجر به ایجاد یکی از مهمترین و مشهورترین تئوریهای موجود در زمینه Design Pattern گردید و خیلی زود با نام Model View Controller یا به اختصار MVC منتشر گردید. SmallTalk اولین کامپایلر شیء گرای محض یا Pure Object Oriented می باشد که متاسفانه تا به امروز افتخار زیارت این موجود افسانه ای و دوست داشتنی نصیب من نشده است!!.
عبارت محض در توصیف این کامپایلر اشاره به این واقعیت بسیار مهم می باشد که بر خلاف سایر کامپایلرهای شی گرا، (مثلا C++، ) امکان کد نویسی به روشهای سنتی Procedural در این کامپایلر وجود ندارد.
حاصل تحقیقات دکتر Reenskaug به تفصیل در heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html قابل مشاهده می باشد. ایشان در قسمت مهمی از این تحقیق جامع چنین توصیفی از تئوری تازه تولد یافته و چالش های نامگذاری آن دارند:

"MVC در حقیقت یک معماری نرم افزار است که به عنوان یک راه حل عمومی بویژه برای برنامه نویسانی که با حجم متنابهی از کد و یا داده ها و اطلاعاتی با ساختارها و ارتباتاط پیچیده روبرو هستند، کاربرد داشته و مفید واقع خواهد شد.
در این مدل تاکید بسیار زیادی بر جدا سازی جزییات منطق ارائه اطلاعات و همه آن چیزی که به عنوان رابط کاربر، موظف به برقراری ارتباط با کاربر و دریافت فرامین او می باشد، شده است.
یکی از چالش ها بعد از تکمیل مدل نهایی، انتخاب یک نام مناسب برای این مدل تازه تولد یافته بود. ابتدا از اصطلاح Model-View-Editor استفاده میکردیم. اما بعد از مباحث متعدد و طولانی که با همکار محترم Adele Goldberg برگذار گردید، پرونده نامگذاری این مدل با انتخاب نام Model View Controller. یا به اختصار MVC برای همیشه بسته شد."

چناتچه از نامگذاری این مدل نیز کاملا مشهود است اجزای اصلی در این مدل عبارتند از Model، View و Controller.

طولی نکشید که این نظریه خود مبنایی برای تفسیر و روایتهای متنوعی گردید که کمابیش برداشتهای متفاوتی از این مدل را ارائه میکردند. به عنوان نمونه مدل Model View Presentation یا MVP یکی از همین موارد می باشد که البته به دلیل تاکید بیشتر در لایه Presentation بیشتر به عنوان مدلی برای ایجاد برنامه های Thick Client که از کتابخانه های توابع رابط کاربر گرافیکی غنی تر و پیشرفته ای بهره می برند، تلقی می شود.
چنانچه گفته شد صرفنظر از برخی جزییات در همه این مدلها خصوصا در MVC اجزای اصلی مدل (چنانکه نام برده شد) یکسان است و البته میتوان به بارز ترین خصوصیات آن به صورت بسیار اجمالی اشاره نمود. اما قبل از معرفی این اجزا لازم میدانم به نکته بسیار مهمی اشاره کنم:

"مدل MVC نیز نظیر اغلب مدل های دیگر با پیش فرض مستقل بودن از Platform و زبان برنامه نویسی ایجاد شده است. من نیز در ابتدا با معرفی عناصر اصلی این مدل، موارد مذکور را رعایت خواهم نمود. اما در نهایت به نظر میرسد برای توصیف دقیق تر جزییات نیاز به ارائه مثالهایی در یک Platform و با بهره گیری از یک زبان مشخص ، اجتناب ناپذیر باشد.
در مدل MVC جزء موسوم به Controller موظف است در یکی از مهمترین وظایف اصلی خود، مفهومی به نام Command را پیاده سازی نماید به نحوی که View قادر به احضار و اجرای فرامین باشد."

در پلتفرم های جدیدتر Silverlight و WPF این مفهوم بنا بر برخی ملاحظاتی که از ابتدای طراحی WPF در نظر بوده (جزییات و دلایل این اختلاف سلیقه را میتوانید به طور مشروح در وبلاگ شخصی مارتین فاولر که خود یکی از تئوریسین ها و طراحان اصلی WPF میباشد مطالعه نمایید) با نام ViewModel نامگذاری شده و مطابق یک روال ثابت، دستیابی به اهداف مورد نظر جهت پیاده سازی Command صرفا با Implement نمودن اینترفیس ICommand قابل دستیابی می باشد. از این به بعد من به مجموعه تکنولوژی های Silverlight و WPF به صورت WPF رجوع خواهم کرد مگر آنکه تفاوتی وجود داشته باشد که آن را صریحا مشخص خواهم نمود.
اجرای هر Command به طور نسبتا رایجی منجر به تغییر یا تعویض مفهوم بسیار مهمی به نام State میگردد. طراحی View به نحوی که متناسب با تغییرات State ، آرایش و چیدمان عناصر رابط کاربر متفاوتی را متناسب با State جدید ارائه نماید، استراتژی بسیار مناسبی است و در این مدل نیز بر آن تاکید ویژه ای شده و توصیه میگردد.
همچنین در Wpf به طور معمول View کلاسی مشتق شده از Control یا UserControl است که مطلوب است Consructor این کلاس تنها شامل یک فراخوانی InitializeComponent (که اجباری نیز هست) باشد. ارتباط View با ViewModel صرفا با مکانیسم کلیدی و بسیار مهم DataBinding برقرار می شود و اغلب رابطه View با ViewModel یک رابطه یک به یک میباشد.
وظیفه Model به طور مشخص ایجاد مکانیسم هایی جهت آگاه نمودن (Notify) اجزای View و Controller است. در Silverlight و WPF این امر با Implement نمودن اینترفیسهای INotifyPropertyChanged و INotifyCollectionChanged میسر میگردد. تجربه شخصی من نشان میدهد که در برخی حالت های خاص با نوعی Data Source مواجه خواهیم شد که امکان اضافه نمودن قابلیت Notify مستقیما میسر نیست. در این وضعیت طراحی ViewModel باید به گونه ای باشد که این نقیصه را جبران کرده و مثلا به صورت یک Wrapper شرایطی فراهم کند که View قادر باشد با بهره گیری از DataBinding وظایف خود را بدون اشکال انجام دهد.
تغییرات View در واکنش به تعویض State مفهوم خیلی غریب و دور از ذهنی نیست. اما به عنوان مثالی برای درک چگونگی تغییر در ViewModel متناسب با تعویض State میتوان تغییر در مجموعه Command هایی که در حال حاضر در دسترس View میباشند را بیان نمود.
البته در حال حاضر (یعنی در WPF 4.5 و Silverlight 5.0) هیچ Template و الگوی استاندارد برای ایجاد یک قالب پروژه سازگار با معماری MVVM وجود ندارد و برای جبران این نقص باید از Library های جداگانه استفاده نمود که یکی از مشهورترین آنها Prism می باشد که در https://compositewpf.codeplex.com/ قابل دسترس بوده و اتفاقا مستندات بسیار کاملی هم همراه این پروژه Open Source میباشد. نسخه 4.2 در حال حاضر اختصاصا برای Framework 4.5 طراحی شده و حتی نسخه اختصاصی آن برای Windows 8 Store Application نیز به تازگی منتشر شده است.
اما یک سوال اساسی در اینجا مطرح میشود و آن اینکه در مقایسه با برنامه های Desktop و با وجود خصلت های متفاوتی که در برنامه های Web-based وجود دارد، آیا امکان استفاده از این مدل در برنامه های مبتنی بر وب یا به طور مشخص در Asp. Net وجود دارد؟
برای پاسخ به سوال مطرح شده به نظر میرسد نگاهی به معماری و نحوه ایجاد برنامه های وب, حداقل با شروع از مقطع زمانی ظهور تکنولوژی Net. ضروریست.
راه حل Microsoft برای تهیه برنامه های وب، در دات نت، مجموعه ای از تکنولوژی ها و ابزارهای خاصی بود که امروزه با نام Classic Asp .Net شناخته میشوند. این تکنولوژی بر اساس مفهومی به نام ViewState طراحی شده است که به عبارت ساده تر تلاشی است جهت غلبه بر مشکلات ناشی از Stateless بودن خود platform وب. به طور مشخص ViewState یک Container ویژه ایست که وضعیت و داده های State جاری در یک Web Form. را حفظ کرده و کل این Container در خلال مکانیسم Postback به وضعیت جدید همان فرم یا یک Web Form جدید منتقل میگردد. این مفهوم تا حدود زیادی حالت موجود در برنامه های Desktop را شبیه سازی کرده و برنامه نویسان را از درگیر شدن با جزییات و تبعات Stateless بودن وب، دور نگاه میدارد.
در اینجا منظور از Postback دقیقا اشاره به یک واحد از Request/Response می باشد که با ایجاد یک Request در Browser و انتقال آن در قالب یک بسته اطلاعاتی ویژه با استفاده از پروتکل HTTP یا HTTPS آغاز شده و بسته مذکور در Web Server دریافت و پردازش میگردد. به طور معمول نتیجه حاصل از پردازش مجددا با استفاده از پروتکل های نام برده شده به Client یا به طور دقیق تر همان Browser، توسط مفهومی به نام Response منتقل میگردد.
در مراحل مختلف این عملیات برنامه نویسان با بهره گیری از مفهومی به نام Event در جریان تغییر جزییات این مراحل قرار میگیرند که به طور معمول با استفاده از مکانیسم Event Handling واکنش ها و عکس العمل های مورد نیاز خود را اعمال می نمایند.
در صورت نیاز به کنترل بیشتر و مدیریت سطوح بالاتر حتی تا حدی که امکان پردازش Request قبل از مکانیسم های پردازش Web Server آغاز شود روش های بسیار موثری نظیر استفاده از Dynamic Request Handling و Modules وجود دارد که دقیقا قابلیت های ISAPI Programming را بدون مواجهه با جزییات مشکل آن میسر میسازند.
این روشها سالهای نسبتا طولانی توسط برنامه نویسان استفاده شده و اتفاقا از محبوبیت زیادی نیز برخوردار می باشند. و قطعا همه دوستان و همکاران محترم بارها از آن در پروژه های گوناگون استفاده نموده اند.
بزرگترین نقد و یا اشکالی که معمولا در باره این روش مطرح میشود این است که چون اساسا در ظاهر برنامه نویسان کدهای مرتبط با Business Logic و Data Access را حتی به همراه کدهای کنترل تراکنشهای مورد نیاز UI در Code Behind ایجاد میکنند، بنابراین به هیچ وجه امکان دستیابی به اهدافی که مثلا در MVC تشریح شده وجود ندارد. به عبارت ساده تر گفته میشود که این تکنولوژی فاقد مکانیسم ها و ابزار لازم جهت دستیابی به هدف Reusability میباشد.
بنا بر تجربه شخصی من، این انتقاد صحیح نیست. شاید تعمیم ایده های مطرح شده در MVC مشخصا در Classic Asp. .Net میسر نباشد اما با اتخاذ روشهای صحیح و مناسب امکان ایجاد برنامه هایی با کد Well Defined در این تکنولوژی امکان پذیر بوده و نمونه های متعددی از آن وجود دارد.
اما استفاده از MVC در وب مبحث دیگریست. برای دستیابی به این هدف، Microsoft با معرفی و انتشار Asp. NET MVC ضمن باز نگری در تکنولوژی قبلی راهکارهای جدیدی متناسب با مدل MVC ارائه نمود. نکته ظریف و بسیار مهمی که در اینجا باید به آن توجه کنیم این است که این "تکنولوژی نه به عنوان یک جایگزین یا Replacement بلکه به عنوان یک راهکار Alternative ارائه شده و مایکروسافت بارها بر روی این نکته تاکید کرده است. قبل از بررسی این تکنولوژی باید متذکر شوم که بنا بر تجربه شخصی پیشنهاد من در صورت نیاز به استفاده از مدل MVC استفاده از تکنولوژی های Silverlight و WPF میباشد که به دلیل بهره گیری از مفاهیم بسیار قدرتمندی مثل حداکثر توانایی در ایجاد UI با استفاده از Declarative Programming که با تکنولوژی Extensible Application Markup Language یا به اختصار XAML (که زمل یا ZAMEL تلفظ میشود) پیاده سازی شده، همچنین مکانیسم های پیشرفته، کارآمد و نوین Data Binding، و. .. دارای حداکثر سازگاری با مدل MVC بوده و تردیدی وجود ندارد که تا سالهای زیاد به عنوان تکنولوژی برتر و توصیه شده مایکروسافت جهت ایجاد برنامه های کاربردی Client/Server در مقیاسهای متنوع مطرح خواهد بود.

در Asp. Net MVC , مایکروسافت دو تغییر بنیادی ایجاد نمود تا این تکنولوژی قابلیت پیاده سازی اهداف MVC را دارا باشد. اول اینکه پروسه postback سنتی و کلاسیک،به طور کلی حذف شده و از Representational State Transfer (که به اختصار REST نامیده میشود) استفاده نمود. دوم اینکه بر خلاف تکنولوژی کلاسیک که در آن الزاما هر URL متناظر با یک. Resource تلقی میشود ( البته به استثنای استفاده از Dynamic HTTP Handler در ASP .NET Web Form کلاسیک که در شرایط خاص این تناظر یک به یک می تواند وجود نداشته باشد و اتفاقا MVC هم با همین روش یعنی HTTP Handler در لایه های میانی و بالای پردازش IIS مستقر میشود) ، در تکنولوژی جدید هر URL صرفا بخشی از Request تلفی شده که معنا و مفهوم دقیق آن کاملا وابسته به نوع پردازشی میباشد که در Controller در نظر گرفته شده است.
در حال حاضر آخرین روایت این تکنولوژی ( روایت 4) علیرغم آنکه همه انتظارها را برآورده نکرده است اما به هر حال به نظر میرسد که اعضای تیم طراحی به سرعت به اهداف مدل نزدیک شده و یک نگاه اجمالی به مستندات و مثالهای مفید آموزشی نشان میدهد که بسیاری از سناریوهای متنوع با تکنولوژی جدید قابل پیاده سازی میباشند.
اما در Silverlight و WPF از همان اولین مراحل طراحی، ملاحظات ضروری جهت رسیدن به بالاترین درجه سازگاری با مدل MVVM لحاظ گردیده است. همچنین چنانچه اشاره شد یکی از فعالیتهای گروه طراحی WPF که به موازات پروژه اصلی دنبال شده در قالب یک پروژه مجزا به نام PRISM به همراه مستندات بسیار آموزنده و چندین پروژه نمونه آماده شده، با موفقیت بی نظیر در حال توسعه میباشد. نظر شخصی من این است که پروژه PRISM در حال حاضر مهم ترین و بهترین منبع آموزشی برای فراگیری مدل MVVM در WPF یا Silverlight می باشد. مخصوصا در حوزه برنامه های Web-based و تواناییهای بالقوه Silverlight در این زمینه، انتخاب آن را برای برنامه نویسانی که از میزان تجربه و مهارت بالایی برخوردار می باشند، اجتناب ناپذیر میسازد. به جرات میتوان ادعا نمود که ترکیب فوق العاده و بسیار هماهنگ منطق MVVM با امکانات ذاتی Silverlight در ارائه UI بسیار چشم نواز و قدرتمند ، در حال حاضر بی رقیب بوده و در میان مجموعه تکنولوژی های مایکروسافت از بهترین ها محسوب میشود.

۰ نظر موافقین ۰ مخالفین ۰ ۳۰ مهر ۹۳ ، ۲۰:۳۰
مهران حسین نیا
دوشنبه, ۷ مهر ۱۳۹۳، ۱۱:۵۲ ق.ظ

کالبد شکافی یک ساختار Dynamic

 

معمولا همه کسانی که در ابتدا با تکنولوژیِ ASP .NET MVC آشنا می شوند در مواجهه با ساختارِ ViewData و ViewBag شگفت زده شده و حتی برخی به دلایلِ متعدد (دلایل مثلی پرهیز از بی نظمی) از استفاده از آن خودداری می کنند .

بعد از مراجعه به منابع و مراجع معتبر فنی آنچه در مورد ساختار ViewBag و دلیل انعطاف پذیری شگفت انگیزِ آن در می یابیم، این است که این ساختار اصطلاحا یک Dynamic Dictionary بوده که قادر است در موارد لازم انواع Property های مختلف را تقریبا بدون هیچ محدودیتی در Type و تعداد پذیرفته و چنانچه همه ما می دانیم در ASP .NET MVC از آن به عنوان یکی از راههای انتقال Data از Controller به View استفاده می شود .

به عنوان مثال، مواردِ زیر در MVC با اینکه کاملا عجیب به نظر می رسند اما کاملا صحیح می باشند :

ViewBag.ThisOne = 10;

ViewBag.AndThisToo = "Some Data";

از سویِ دیگر در مبحث Dynamic و در C# ما ابتدا در روایت سوم (C# 3.0) با Keyword یا کلمه کلیدی var و در روایت چهارم با Keyword جدید تر dynamic روبرو هستیم . اما از مهمترین سوالهایی که برای  همه مطرح می شود این است که اولا تفاوت این دو در چیست و ثانیا چگونه می توان یک ساختار dynamic با عملکردی مشابه با ViewBag ایجاد کرد؟

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

 

namespace ConsoleApplication

{

    class SimpleEmployee

    {

        public int Id { get; set; }

        public string FirstName { get; set; }   

    } 

 

    class Program

    {

 

        static void Main(string[] args)

        {           

            var employee = new SimpleEmployee() { Id = 1, FirstName = "Mehran"};

            dynamic employee2;

            employee2 = new SimpleEmployee() { Id = 1, FirstName = "Mehran"};

            Console.WriteLine("—-- Using var -----------");

            Console.WriteLine("Employee First Name:" + employee.FirstName);

            // Compile time Error

            //Console.WriteLine("Employee Last Name:" + employee.LastName);

            Console.WriteLine("—-- Using dynamic -----------");

            Console.WriteLine("Employee2 FirstName:" + employee2.FirstName);

            // Compile successfully but it will throw exception at runtime

            Console.WriteLine("Employee Last Name:" + employee2.LastName);

            Console.ReadLine();

        }

    }

}

 

در مثال قبل شواهدی وجود دارد که می توان بر اساس آنها پاسخ سوال اول را مشخص نمود. اولا متغیر داینامیک تعریف شده با var حتما در همان لحظه معرفی باید دارای مقدار باشد که در مورد dynamic چنانچه در مثال به روشنی مشخص شده این وضعیت اجباری نیست. ثانیا عدم وجود LastName در آبجکت employee که با var ایجاد شده در همان زمان کامپایل با مشکل روبرو میشود در حالیکه در آبجکت employee2 تا قبل از اجرا ، کامپایلر هیچ شکایتی از عدم وجود LastName نمی کند . مورد دیگری که باید به آن توجه کرد این است که آبجکت ایجاد شده با var از حمایت Intellisense برخوردار است و در آبجکت ایجاد شده با dynamic این وضعیت وجود ندارد .

اما با اینکه در هر دو حالت ما یک Dynamic Object ایجاد کرده ایم هنوز اساسا قادر نیستیم که درست نظیر ViewBag از آن استفاده کنیم . پس باید تغییراتی در کد ایجاد کنیم.

 

هیچ تردیدی نیست که منشاء این رفتار یا خاصیت شگفت انگیز آبجکت های Dynamic را که وولورین Object Oriented محسوب میشود باید در وراثت جستجو کرد. حتی با یک بررسی ساده در سلسله مراتب Ancestor ها و اجداد این جهش فوق العاده می توان دریافت که نقش IDynamicMetaObjectProvider چقدر حیاتی و مهم می باشد .

در واقع کلمه کلیدی dynamic باعث میشود که DLR یا همان Dynamic Library Runtime ماهیت بسیار متفاوتی برای Objectی که با dynamic معرفی شده ، تعیین کند و این همه توضیح صرفا در اصطلاح Late Binding خلاصه میشود .

پس آنچه مسلم است به منظور دستیابی به یک Dynamic Object تمام عیار ، باید توجه خود را به  IDynamicMetaObjectProvider معطوف نماییم . زیرا این اینترفیس و متدهای موجود در آن به یک کلاس این امکان را می دهند که رفتار Late Bind از خود نشان داده و DLR مراقب و کنترل کننده این رفتار خواهد بود .  خوشبختانه کلاسهای DynamicObject و ExpandoObject هر دو این Interface را Implement کرده اند و بنا براین بهتر است این دو مورد را با دقت بیشتری بررسی کنیم .  زیرا این دو کلید دستیابی به پاسخ سوال دوم ما هستند و به واسطه همین دو کلاس ما قادر خواهیم بود که چرخ را دوباره اختراع کرده و رفتاری مثل رفتار ViewBag را احیانا در کلاسهای مورد نیاز خود تقلید  کنیم .

قبل از گسترش مثال قبلی خود و در مقام مقایسه این دو کلاس باید اضافه کنم که ExpandoObject بر خلاف DynamicObject یک لقمه آماده بوده و برای رسیدن به هدفی که در نظر داریم شاید بهترین گزینه باشد . اما اگر احیانا در نظر داشته باشیم که بنا به دلایل متعدد، کنترل کامل این وضعیت را بر عهده داشته باشیم لازم است کلاسی وارث کلاس DynamicObject ایجاد کرده و با Override نمودن متدهای TryGetMember و TrySetMember نهایت انعطاف لازم را ایجاد کنیم
تقریبا مشابه این عملیات در یکی از مثالهای MSDN به سادگی نشان داده شده است و مثال ما از نظر هدفی که دنبال میکند دقیقا مشابه آن است . این مثال را در 
http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.trygetmember(v=vs.110).aspx
می توانید دنبال کنید .
در مثال بعد جزییات این کار و گسترش کلاسی که در ابتدا ایجاد کرده بودیم، نشان داده شده است :

 

    class SimpleEmployee : DynamicObject

    {

        private readonly Dictionary<string, dynamic> _properties = new Dictionary<string, dynamic>(StringComparer.InvariantCultureIgnoreCase);

 

        public override bool TryGetMember(GetMemberBinder binder, out dynamic result)

        {

            result = this._properties.ContainsKey(binder.Name) ? this._properties[binder.Name] : null;

 

            return true;

        }

 

        public override bool TrySetMember(SetMemberBinder binder, dynamic value)

        {

            if (value == null)

            {

                if (_properties.ContainsKey(binder.Name))

                    _properties.Remove(binder.Name);

            }

            else

                _properties[binder.Name] = value;

 

            return true;

        }

        public int Id { get; set; }

        public string FirstName { get; set; }

    }

 

چنانچه مشاهده میکنید گسترش کلاس ساده SimpleEmployee ما که اینک شاید سادگی صفت مناسبی برای توصیف آن نباشد در سه مرحله انجام شده است:

در قدم اول یک Data Member به نام _properties با دسترسی private از نوع Dictionary<string, dynamic> اضافه شده است .  این در حقیقت Container اصلی همه Property هاییست که قادر هستند به صورت Dynamic به کلاس ما اضافه شوند . فقط لازم است در اینجا به یک نکته مهم توجه کنیم که بواسطه استفاده از StringComparer.InvariantCultureIgnoreCase همه Property هایی که به صورت Dynamic اضافه میشوند بدون استثنا Case Insensitive هستند و هیچ حساسیتی در مطابقت حروف بزرگ و کوچک در آنها وجود ندارد . این موضوع در مثال بعدی با تاکید نشان داده شده است .

در اقدام بعدی متدهای TryGetMember و TrySetMember را در کلاس خود Override کرده و تقریبا مشابه با وضعیت معمول استفاده از متد های get و set از آنها استفاده میکنیم . نوع دسترسی public در آنها نباید این شبهه را بوجود بیاورد که استفاده از Property های Dynamic صرفا با فراخوانی این دو متد میسر می باشد . در واقع ما با استفاده از Syntax رایج و مشهور اپراتور دات (.) از Property های ایجاد شده استفاده خواهیم کرد و DLR فراخوانی این دو متد را به صورت ضمنی (Implicit) انجام خواهد داد:

 

    class Program

    {

 

        static void Main(string[] args)

        {

            dynamic employee;

            employee = new SimpleEmployee() { Id = 1, FirstName = "Mehran"};

            employee.LastName = "Hossein nia";

            employee.MyBadHabit = "Smoking";

            employee.Salary = 900000;

            employee.HireDate = DateTime.Now.AddYears(-15);

            employee.IJustRememberedNow = "I don't have boss";

            employee.EchoMessage = new Action<string>(s =>

            {

 

                Console.WriteLine(s);

 

            });

 

            Console.WriteLine("Employee FirstName:" + employee.FirstName);

            Console.WriteLine("Employee Last Name:" + employee.LastName);

            Console.WriteLine("Employee Last Name:" + employee.lAsTnAme);                       Console.WriteLine("Employee Bad Habit:" + employee.MyBadHabit);

            Console.WriteLine("Employee Salary:" + employee.Salary);

            Console.WriteLine("Employee Hire Date:" + employee.HireDate);

            Console.WriteLine("Employee IJustRememberedNow:" + employee.IJustRememberedNow);

            employee.EchoMessage("Simply Show this message at the end.");

 

            Console.ReadLine();

 

        }

    }

 

تقریبا به استثنای متد EchoMessge درباره همه اجزای این مثال در حد بضاعت توضیحاتی داده شده است . اما در حین نوشتن این مثال به نظرم رسید که  می توان با یک ترفند ساده این ویژگی قابلیت افزایش Dynamic اعضای کلاس را صرفا محدود به Property ها نکرده و با استفاده از متدهای Anonymous انعطاف این کلاس را افزایش داد. چنانچه در کد بوضوح قابل مشاهده است نتیجه این کار حتی فراتر از تصور خودم بود و در حال حاضر هیچ توضیحی برای انگیزه استفاده از آن ندارم .

همانطور که قبلا هم اشاره شد استفاده از ExpandoObject بسیار ساده و صرفا با یک نمونه سازی میسر است . در زیر نمونه ای از آن نشان داده شده است :


 

            dynamic contact = new ExpandoObject();

            contact.NewProperty = "I am new";

            contact.WhatEverYouWant = "Real Freedom";

 

۰ نظر موافقین ۰ مخالفین ۰ ۰۷ مهر ۹۳ ، ۱۱:۵۲
مهران حسین نیا
پنجشنبه, ۳ مهر ۱۳۹۳، ۰۱:۴۹ ب.ظ

گزارش یک تجربه در MVC

پس از گذشت سه ماه از نصب و اجرای موفقیت آمیز سامانه مانیتورینگ ، داشبورد و Help desk شهرداری منظقه شش، و در خلال رصد دقیق قسمتهای مختلف برنامه، دومین نسخه این نرم افزار با ارتقا به ASP .NET MVC 5 به پایان رسید .
این ارتقا در بر گیرنده تغییر ماهیت همه Action ها به وضعیت Asynchronous و تبدیل کلیه متدهای موجود در Controller ها به وضعیت Awaitable بوده است . همچنین سیستم Security Management این نرم افزار از Simple Membership به Identity تغییر کرد .
یکی از اهداف اصلی در نسخه دوم امکان Responsive بودن UI و استفاده از این نرم افزار بر روی تبلت ها و گوشی های هوشمند Android بوده است که در ساده ترین حالت امکان استفاده از Neo Barcode ها و اسکن آنها جهت شناسایی تعداد 1000 کامپیوتر موجود در ساختمان مرکزی و سایر نواحی تابعه، فراهم گردد.
این ویژگی علاوه بر سهولت پروسه به روز رسانی اطلاعات تجهیزات در هنگام ارائه سرویس های نرم افزاری ، سخت افزاری و شبکه، سرعت ارائه این خدمات را تا حد بسیار مطلوبی افزایش خواهد داد.
من در اولین طرح تحلیل گردش کار، State های متفاوتی را برای ثبت یک درخواست خدمات و سرویس در نظر گرفتم که مهم ترین هدف من علاوه بر امکان ایجاد کارتابل های کاملا اختصاصی برای سطوح دسترسی متفاوت، امکان اندازه گیری زمان انتظار هر درخواست در State های مختلف فراهم باشد . این موضوع بسیار از جانب مدیران سطوح بالاتر مورد استقبال واقع شد و در نسخه های بعدی به طور دقیق تری امکان ایجاد State های مکمل جهت افزایش دقت در پروسه ارائه سرویس را فراهم خواهد کرد .

همچنین به دلیل اهمیت شاخص های مانیتورینگ و پرهیز از هر گونه اتلاف وقت، سیستم مجهز به یک اعلان هشدار (Notify) می باشد که فعلا در سه گروه اطلاع، هشدار و خظر طراحی و ایجاد شده است .
با توجه به اینکه دقت و وسواس در جزییات UI در تخصص من نبوده و نیست، استفاده از Bootstrap و به کار گیری کامپوننت های amChart در نمودارها و Telerik در Neo Barcode کمک بسیار موثری در بهبود کیفیت UI بوده اما هنوز هم با همه این اوصاف توجه به این مقوله در مقایسه با سایر بخشهای فنی شاید بیش از 70 درصد از انرژی و حوصله مرا مصروف خود ساخت که از این بابت اصلا راضی نیستم.

 

۰ نظر موافقین ۰ مخالفین ۰ ۰۳ مهر ۹۳ ، ۱۳:۴۹
مهران حسین نیا
پنجشنبه, ۳ مهر ۱۳۹۳، ۰۱:۳۶ ب.ظ

برنامه نویسی

 

به نظرم هنر برنامه نویسی، عبارت است از مهارتهای لازم برای استفاده از یک یا چند زبان برنامه نویسی که برای وادار کردن کامپیوتر به اجرای مجموعه ای از دستورالعمل ها که معمولا برای انجام یک وظیفه یا رسیدن به یک هدف خاص نوشته شده و مورد استفاده قرار می گیرد.
مخصوصا در ایران ، اصطلاح برنامه نویس به کسی اطلاق میشود که در زمینه طراحی و تولید برنامه های کاربردی فعالیت میکند و تقریبا به صورت رایج هدف نهایی، تبدیل مجموعه ای از روتین هایی است که در ابتدا به صورت سنتی و با استفاده از فرمهای کاغذی و یا تعامل بین اعضایی که در یک شغل یا Business خاص فعالیت میکنند، وجود دارند و سپس به فرمهای تعاملی در کامپیوتر و ثبت الکترونیکی جزییات این تعامل و در نهایت امکان تهیه گزارش از این اطلاعات تبدیل میشوند.
برنامه های کاربردی در موضوعاتی مثل حسابداری، انبار داری، امور دفتری کلینیک ها و بیمارستانها و .... از رایج ترین سناریو های این نوع از برنامه ها محسوب میشوند . در ساده ترین شکل خود این نوع از برنامه های کاربردی دارای دو مولفه یا جزء اصلی هستند که به طور کلی می توان آنها را Business And Data Access Code و Data Source تقسیم کرد . بدیهی است که استفاده از برخی تئوری ها و مدل هایی که عموما به Design Patterns مشهور هستند کل ساختار یک برنامه را به مولفه های متفاوتی طبقه بندی میکنند که مدلهای n-Layers و MVC و MVVM از مشهورترین این مدلها هستند . اما به نظر می رسد صرفنظر از این تئوری ها ماهیت دو موردی که نام برده شد کاملا دارای ماهیت متفاوتی هستند و بنابراین شاخص تر به نظر می رسند.
انگیزه اصلی نوشتن این پست این بود که اخیرا احیانا یک نوع سوء تعبیر از برنامه نویسی بواسطه مطالبی که اخیرا در گروهها منتشر شده در ذهن من ایجاد شد که به نظرم آمد با اینکه این مطالب بسیار بدیهی هم هستند اما شاید یک اشاره کوتاه به تلقی شخصی خودم در مورد این موضوع، خالی از لطف نباشد.
در سایر شاخه های برنامه نویسی دغدغه هایی مثل استفاده از Componets ، درگیر شدن با جزییات نحوه آرایش فرم ها و Dialog Box ها اساسا وجود ندارد و یا اگر وجود داشته باشد حداقل قابل مقایسه با دغدغه های مرتبط در برنامه های کاربردی (Application) به معنای عام این نوع از برنامه ها، نیست .
به نظر من حتی در زبان انگلیسی هنوز اصطلاح Application به معنای عام خود در مورد همه نرم افزارهای کاربردی اشاره میکند و هیچ تمایزی بین زیر شاخه های اصلی برنامه نویسی در این اصطلاح وجود ندارد مگر آنکه صریحا مثلا به صورت Network Applications یا System Application با یک پیشوند این تمایز نشان داده شود .
در Network Applications معمولا موضوع اصلی فراگیری کامل TCP/IP (به عنوان مدل هفت لایه استاندارد) یا مدل چهار لایه همین پروتکل به نام OSI می باشد که معمولا برنامه نویسان مخصوصا با زوم کردن بر روی دو پروتکل اصلی لایه Transport یعنی TCP و UDP و استفاده از قابلیتهای SDK در OS مثل Socket Programming سعی در طراحی و اجرای سناریو های رایج در این شاخه جذاب از برنامه نویسی ، می کنند . از مشهورترین برنامه های تجاری در این زمینه می توان به برنامه های FTP Client ، Messanger ها ، App های مختص VOIP و ارتباطات تلفنی و تصویری و ... اشاره کرد .
نظیر همین وضعیت در شاخه های دیگر برنامه نویسی مثل System Applications و Game Applications نیز وجود دارد .
مثلا در System Application موضوعات از طیف بسیار گسترده ای مثل Device Driver ها Benchmark ها و Optimizer ها و ... برخوردار است و موضوع اصلی شامل توابع سطح پایین OS و SDK ها می باشد که جهت ارتباط با پورت ها و در نهایت راه اندازی Device های مرتبط به این پورت ها مورد استفاده قرار می گیرند .
در Game Application (مخصوصا Game های Windows و کنسول های تخصصی مبتنی بر Windows) تقریبا همیشه DirectX و قابلیتهای منحصر بفرد آن در گرافیک هدف اصلی برنامه نویسان این رشته بوده که باز تاکید میکنم دغدغه های کاملا متفاوتی با برنامه نویسان Application های کاربردی وجود دارد .
تکنیک هایی مثل 3D Meshing And Rendering ، نورپردازی یا Ray Tracing و محاسبات مرتبط با انعکاس یا Reflection و سایه ها یا Shadows دنیای بسیار متفاوتی با موضوعات برنامه های کاربردی دارند .
مجال پرداختن به جزییات دیگر در اینجا وجود ندارد و حقیقتا حتی در حوزه تخصصی من هم نیست اما واقعا به نظرم رسید ذکر این بدیهیات اگر سودی نداشته باشد حداقل بی ضرر می باشد .

۰ نظر موافقین ۰ مخالفین ۰ ۰۳ مهر ۹۳ ، ۱۳:۳۶
مهران حسین نیا
چهارشنبه, ۱۱ تیر ۱۳۹۳، ۱۱:۴۹ ق.ظ

دردسرهای Simple Membership در MVC

بر خلاف تجربه های بی دردسر استفاده از SqlMembership به نظر می رسد روشهای استاندارد استفاده از SimpleMembership مخصوصا در MVC با مشکلاتی روبرو می باشد که از میان آنها می توان به موارد زیر اشاره کرد:
اول آنکه چنانچه مطلع می باشید در Template استاندارد یک پروژه MVC وظایف مورد نیاز جهت Initialize نمودن SimpleMembership بر اساس مفهوم Filter ها ایجاد شده است که مشخصا در فولدر Filters ، کلاس InitializeSimpleMembershipAttribute عهده دار انجام این وظیفه می باشد . اما چنانچه صریحا در قالب یک کامنت مشاهده می کنیم :



Create the SimpleMembership database without Entity Framework migration schema
 


به دلایلی Table های ایجاد شده به استثنای UserProfile فاقد Schema و Metadataی مورد نیاز جهت استفاده در Entity Framework می باشند که با توجه به نیاز مبرمی که برای تطبیق Simple Membership با Business Models وجود دارد این کار با اشکال روبرو خواهد شد و نیاز به استفاده از روشهای دیگر ضروری به نظر می رسند .
به عنوان مثال مفهوم Roles و نقش ها که مبنای اصلی Authorization در ملاحظات امنیتی برنامه ها می باشد صرفا با استفاده از متدهای استاتیک کلاس Roles میسر می باشند که اغلب نیاز های موجود را برطرف نمی سازد. البته مورد ذکر شده دقیقا به همین شکل در Sql Membership و بدون تغییر به Simple Memebership منتقل شده است . تجربه شخصی من این است که در مباحث پیاده سازی Authorization (ایجاد نقش ها و گروههای امنیتی، عضویت کاربران در گروهها، بررسی مجوزهای کاران بر اساس مجوز های منتسب به گروهها و...) استفاده از Flagged Enumeration ها در C# بسیار کارآمد و قابل اطمینان می باشد . با استفاده از این مفهوم همچنین اختصاص کاربران به گروهها (Roles) ، بررسی عضویت یا عدم عضویت کاربران در Role ها و ... صرفا با استفاده از اپراتورهای And و OR بیتی (Bitwise) میسر خواهند بود : در زیر یک نمونه از این ساختار را مشاهده می نمایید :


[Flags]
public enum HelpdeskPermissions
{
HP_Anonymous = 0,
HP_SiteMaster = 1,
HP_SiteAdmin = 2,
HP_SiteExpert=4,
HP_Associates=8,
HP_RegionUser=16,
HP_GISDemo=32,
HP_GISAdmin=64,
HP_Manager = 256
}


چنانچه در ابتدا نیز اشاره شد پیاده سازی مقدار دهی اولیه SimpleMemebership با استفاده از مفهوم Filter ها و به طور مشخص Action Filter و بهره گیری از روش Overload کردن متد OActionExecuting در کلاس منشعب از ActionFilterAttribute بعد از Publish نمودن برنامه در IIS منجر به بروز مشکلاتی میشود که برای برطرف نمودن آن آسانترین راه ممکن، صرفنظر کردن از Filter ها و انتقال متد استاتیک InitializeDatabaseConnection از کلاس WebSecurity به داخل متد Application_Start در Global.asax می باشد .
با این روش امکان استفاده از Simple Membership با نسخه های SQL Server و امکان Publish بی دردسر Application در IIS فراهم خواهد شد .

۱ نظر موافقین ۰ مخالفین ۰ ۱۱ تیر ۹۳ ، ۱۱:۴۹
مهران حسین نیا