مطالب
استفاده از LocalDb در IIS، قسمت دوم: مالکیت وهله ها
در قسمت قبلی این مقاله گفتیم که دو خاصیت از LocalDb هنگام استفاده از Full IIS باعث بروز خطا می‌شوند:

  • LocalDb نیاز دارد که پروفایل کاربر بارگذاری شده باشد
  • بصورت پیش فرض وهله LocalDb متعلق به یک کاربر بوده، و خصوصی است

در قسمت قبل دیدیم چگونه باید پروفایل کاربر را بدرستی بارگذاری کنیم. در این مقاله به مالکیت وهله‌ها (instance ownership) می‌پردازیم.


مشکل وهله خصوصی

در پایان قسمت قبلی، اپلیکیشن وب را در این حالت رها کردیم:

همانطور که مشاهده می‌کنید با خطای زیر مواجه هستیم:

System.Data.SqlClient.SqlException: Cannot open database "OldFashionedDB" requested by the login. The login failed.
Login failed for user 'IIS APPPOOL\ASP.NET v4.0'. 

این بار پیغام خطا واضح و روشن است. LocalDb با موفقیت اجرا شده و اپلیکیشن وب هم توانسته به آن وصل شود، اما این کانکشن سپس قطع شده چرا که دسترسی به وهله جاری وجود نداشته است. اکانت ApplicationPoolIdentity (در اینجا IIS APPPOOL\ASP.NET v4.0) نتوانسته به دیتابیس LocalDb وارد شود، چرا که دیتابیس مورد نظر در رشته اتصال اپلیکیشن (OldFashionedDB) وجود ندارد. عجیب است، چرا که وصل شدن به همین دیتابیس با رشته اتصال جاری در ویژوال استودیو با موفقیت انجام می‌شود.

همانطور که در تصویر بالا مشاهده می‌کنید از ابزار SQL Server Object Explorer استفاده شده است. این ابزار توسط SQL Server Data Tools معرفی شد و در نسخه‌های بعدی ویژوال استودیو هم وجود دارد و توسعه یافته است. چطور ممکن است ویژوال استودیو براحتی بتواند به دیتابیس وصل شود، اما اپلیکیشن وب ما با همان رشته اتصال نمی‌تواند دیتابیس را باز کند؟ در هر دو صورت رشته اتصال ما بدین شکل است:

Data Source=(localdb)\v11.0;Initial Catalog=OldFashionedDB;Integrated Security=True

پاسخ این است که در اینجا، دو وهله از LocalDb وجود دارد. بر خلاف وهله‌های SQL Server Express که بعنوان سرویس‌های ویندوزی اجرا می‌شوند، وهله‌های LocalDb بصورت پروسس‌های کاربری (user processes) اجرا می‌شوند. هنگامی که کاربران مختلفی سعی می‌کنند به LocalDb متصل شوند، برای هر کدام از آنها پروسس‌های مجزایی اجرا خواهد شد. هنگامی که در ویژوال استودیو به localdb)\v11.0) وصل می‌شویم، وهله ای از LocalDb ساخته شده و در حساب کاربری ویندوز جاری اجرا می‌شود. اما هنگامی که اپلیکیشن وب ما در IIS می‌خواهد به همین دیتابیس وصل شود، وهله دیگری ساخته شده و در ApplicationPoolIdentity اجرا می‌شود. گرچه ویژوال استودیو و اپلیکیشن ما هر دو از یک رشته اتصال استفاده می‌کنند، اما در عمل هر کدام به وهله‌های متفاوتی از LocalDb دسترسی پیدا خواهند کرد. پس مسلما دیتابیسی که توسط وهله ای در ویژوال استودیو ساخته شده است، برای اپلیکیشن وب ما در IIS در دسترس نخواهد بود.

یک مقایسه خوب از این وضعیت، پوشه My Documents در ویندوز است. فرض کنید در ویژوال استودیو کدی بنویسیم که در این پوشه یک فایل جدید می‌سازد. حال اگر با حساب کاربری دیگری وارد ویندوز شویم و به پوشه My Documents برویم این فایل را نخواهیم یافت. چرا که پوشه My Documents برای هر کاربر متفاوت است. بهمین شکل، وهله‌های LocalDb برای هر کاربر متفاوت است و به پروسس‌ها و دیتابیس‌های مختلفی اشاره می‌کنند.

به همین دلیل است که اپلیکیشن وب ما می‌تواند بدون هیچ مشکلی روی IIS Express اجرا شود و دیتابیس را باز کند. چرا که IIS Express درست مانند LocalDb یک پروسس کاربری است. IIS Express توسط ویژوال استودیو راه اندازی می‌شود و روی حساب کاربری جاری اجرا می‌گردد، پس پروسس آن با پروسس خود ویژوال استودیو یکسان خواهد بود و هر دو زیر یک اکانت کاربری اجرا خواهند شد.


راه حل ها

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


رویکرد 1: اجرای IIS روی کاربر جاری ویندوز

اگر مشکل، حساب‌های کاربری مختلف است، چرا خود IIS را روی کاربر جاری اجرا نکنیم؟ در این صورت ویژوال استودیو و اپلیکیشن ما هر دو به یک وهله از LocalDb وصل خواهند شد و همه چیز بدرستی کار خواهد کرد. ایجاد تغییرات لازم نسبتا ساده است. IIS را اجرا کنید و Application Pool مناسب را انتخاب کنید، یعنی همان گزینه که برای اپلیکیشن شما استفاده می‌شود.

قسمت Advanced Settings را باز کنید:

روی دکمه سه نقطه کنار خاصیت Identity کلیک کنید تا پنجره Application Pool Identity باز شود:

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

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


رویکرد 2: استفاده از وهله مشترک

یک راه حال دیگر استفاده از قابلیت instance sharing است. این قابلیت به ما این امکان را می‌دهد تا یک وهله LocalDb را بین کاربران یک سیستم به اشتراک بگذاریم. وهله به اشتراک گذاشته شده، توسط یک نام عمومی (public name) قابل دسترسی خواهد بود.

ساده‌ترین راه برای به اشتراک گذاشتن وهله‌های LocalDb استفاده از ابزار SqlLocalDB.exe است. بدین منظور Command Prompt را بعنوان مدیر سیستم باز کنید و فرمان زیر را اجرا نمایید:

sqllocaldb share v11.0 IIS_DB
این فرمان وهله خصوصی LocalDb را با نام عمومی IIS_DB به اشتراک می‌گذارد. حال تمام کاربران سیستم می‌توانند با آدرس localdb)\.\IIS_DB) به این وهله وصل شوند. این فرمت آدرس دهی سرور دیتابیس، مشخص می‌کند که از یک وهله shared استفاده می‌کنیم. رشته اتصال جدید مانند لیست زیر خواهد بود:

Data Source=(localdb)\.\IIS_DB;Initial Catalog=OldFashionedDB;Integrated Security=True

پیش از آنکه اپلیکیشن وب ما بتواند به این وهله متصل شود، باید لاگین‌های مورد نیاز برای ApplicationPoolIdentity را ایجاد کنیم. راه اندازی وهله ساده است، کافی است دیتابیس را در SQL Server Object Explorer باز کنید. این کار اتصالی به دیتابیس برقرار می‌کند و آن را زنده نگاه می‌دارد. برای ایجاد لاگین مورد نظر، می‌توانیم در SQL Server Object Explorer یک کوئری اجرا کنیم:

create login [IIS APPPOOL\ASP.NET v4.0] from windows;
exec sp_addsrvrolemember N'IIS APPPOOL\ASP.NET v4.0', sysadmin

اسکریپت بالا به اکانت ApplicationPoolIdentity سطح دسترسی کامل می‌دهد. در صورت امکان بهتر است از سطوح دسترسی محدود‌تری استفاده کنید، مثلا دسترسی به دیتابیس یا جداولی مشخص. حالا می‌توانید اپلیکیشن را مجددا اجرا کنید و همه چیز بدون خطا باید کار کند.

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


رویکرد 3: استفاده از SQL Server Express

از آنجا که نسخه کامل SQL Server Express بعنوان یک سرویس ویندوزی اجرا می‌شود، شاید بهترین راه استفاده از همین روش باشد. کافی است یک نسخه از SQL Server Express را نصب کنیم، دیتابیس مورد نظر را در آن بسازیم و سپس به آن متصل شویم. برای این کار حتی می‌توانید از ابزار جدید SQL Server Data Tools استفاده کنید، چرا که با تمام نسخه‌های SQL Server سازگار است. در صورت استفاده از نسخه‌های کامل تر، رشته اتصال ما بدین شکل تغییر خواهد کرد:

Data Source=.\SQLEXPRESS;Initial Catalog=OldFashionedDB;Integrated Security=True
مسلما در این صورت نیز، لازم است اطمینان حاصل کنیم که ApplicationPoolIdentity به وهله SQL Server Express دسترسی کافی دارد. برای این کار می‌توانیم از اسکریپت قبلی استفاده کنیم:

create login [IIS APPPOOL\ASP.NET v4.0] from windows;
exec sp_addsrvrolemember N'IIS APPPOOL\ASP.NET v4.0', sysadmin

حال اجرای مجدد اپلیکیشن باید با موفقیت انجام شود. استفاده از این روش مسلما امکان استفاده از LocalDb را از ما می‌گیرد. ناگفته نماند که وهله‌های SQL Server Express همیشه در حال اجرا خواهند بود چرا که بصورت سرویس‌های ویندوزی اجرا می‌شوند. همچنین استفاده از این روش ممکن است شما را با مشکلاتی هم مواجه کند. مثلا خرابی رجیستری ویندوز می‌تواند SQL Server Express را از کار بیاندازد و مواردی از این دست. راهکار‌های دیگری هم وجود دارند که در این مقاله به آنها نپرداختیم. مثلا می‌توانید از AttachDbFilename استفاده کنید یا از اسکریپت‌های T-SQL برای استفاده از وهله خصوصی ASP.NET کمک بگیرید. اما این روش‌ها دردسر‌های زیادی دارند، بهمین دلیل از آنها صرفنظر کردیم.


مطالعه بیشتر درباره LocalDb

نظرات مطالب
بررسی خطاهای ممکن در حین راه اندازی اولیه برنامه‌های ASP.NET Core در IIS
بنده هم هنگام پابلیش بر روی Windows Server 2012 پیغام 502.5 مشاهده کردم . با زدن دستور dotnet myapp.dll

 خطای زیر را مشاده کردم .
  An assembly specified in the application dependencies manifest (MyWebApp.deps.json) was not found:
    package: 'Microsoft.ApplicationInsights.AspNetCore', version: '2.1.1'
    path: 'lib/netstandard1.6/Microsoft.ApplicationInsights.AspNetCore.dll'
  This assembly was expected to be in the local runtime store as the application was published using the following target manifest files:
راه‌های گفته شده را امتحان کردم نشد و سپس با جستجوی بیشتر  متوجه شدم باید دستور زیر را به .csproject برنامه اضافه کرد و بعد دوباره پابلیش کردم و مشکل بر طرف شد. اما این روش در هنگام پابلیش فایل‌های بیشتری تولید می‌کند و پروژه را سنگین می‌کند. آیا راه حل دیگری برای این پیغام بالا وجود دارد ؟
<PropertyGroup>               
    <PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest>
</PropertyGroup>

مطالب
مقایسه ساختاری دو دیتابیس SQL Server

یکی از مواردی که در محیط کاری زیاد پیش می‌آید بحث همگام نبودن دیتابیس توسعه با دیتابیس کاری است.
منظور از دیتابیس توسعه، همان دیتابیسی است که برای برنامه نویسی و آزمایش از آن استفاده می‌شود و دیتابیس کاری هم مشخص است (برای مثال بر روی یک سرور در اینترانت داخلی یک شرکت و یا بر روی یک سرور اینترنتی قرار دارد). عادت‌های مختلفی هم این‌جا ممکن است وجود داشته باشد، برای مثال تغییرات جدید بر روی دیتابیس کاری اعمال شود و سپس فراموش شود که همان‌ها نیز باید به دیتابیس توسعه هم اعمال شوند تا در تغییرات بعدی برای آزمایش دچار مشکل نشویم و برعکس. بعد از یک مدت هم تبدیل به کابوس می‌شود؛ نمی‌دانیم الان دیتابیس کاری جدیدتر است یا دیتابیس توسعه؛ و یا اینکه کلا دو دیتابیس مفروض چه تفاوت‌های ساختاری با هم دارند (بدیهی است بحث دیتا در اینجا در درجه‌ی اول اهمیت قرار ندارد). فرصت این هم وجود ندارد که تک تک جداول، ویووها، رویه‌های ذخیره شده و خلاصه تمامی اشیاء مرتبط را بررسی کنیم که چه اختلافی با هم دارند. اینجا مستندات هم کمکی نخواهند کرد چون صحبت از یک جدول با 5 فیلد در میان نیست که موارد را سریع و به صورت دستی تطابق دهیم. همچنین این مشکل عموما زمانی رخ می‌دهد که یکی از دو طرف در حال حاضر مستندات کامل و به روزی ندارد. اکنون چه باید کرد؟
اولین فکری که به ذهن خطور می‌کند مراجعه به ابزارهای جانبی است (مثلا Red Gate's SQL Compare چند صد دلاری) غافل از اینکه خود Visual studio 2008 (نگارش‌های تیمی و دیتابیسی) این قابلیت را نیز ارائه می‌دهد (شکل زیر).


پس از انتخاب new schema comparison ، در صفحه‌ای که ظاهر می‌شود، بر روی new connection کلیک کرده و دیتابیس‌های مبداء و مقصد را جهت مقایسه ساختاری انتخاب نمائید و سپس بر روی دکمه Ok کلیک کنید.


اگر اس کیوال سرور 2008 را نصب کرده باشید، با پیغام زیر روبرو خواهید شد:


برای رفع این مشکل باید بسته به روز رسانی زیر را نصب کرد تا این نگارش نیز پشتیبانی شود:

(برای نصب حتما باید SP1 مربوط به VS.Net 2008 پیشتر نصب شده باشد)

پس از کلیک بر روی دکمه Ok، کار آنالیز دو دیتابیس شروع شده و تفاوت‌ها گزارش داده می‌شوند:


همچنین جهت سهولت کار، اسکریپت T-SQL ایی را نیز به نام schema update script تولید می‌کند که با اجرای آن به سادگی کار به روز رسانی دیتابیس مقصد صورت خواهد گرفت.


در پایان یا می‌توان اسکریپت تولید شده را ذخیره کرد و در زمانی دلخواه اجرا و اعمال نمود و یا می‌توان بلافاصله بر روی دکمه write updates که در نوار ابزار ظاهر شده است کلیک کرد تا دیتابیس مقصد از لحاظ ساختاری با دیتابیس مبداء یکی شود.



اشتراک‌ها
فعال سازی تست اتماتیک در ویژوال استودیو

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

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

وبلاگ‌ها و سایت‌های ایرانی

امنیت



ASP. Net


طراحی وب

PHP


اس‌کیوال سرور


سی شارپ


عمومی دات نت


مسایل اجتماعی و انسانی برنامه نویسی


کتاب‌های رایگان جدید


متفرقه
  • آهن بجای کروم! (یک برنامه نویس آلمانی قسمت‌هایی از مرورگر کروم را که در جهت جمع آوری اطلاعات برای گوگل بکار می‌رفته، حذف کرده و مرورگر دیگری به نام آهن را ارائه داده است!)

مطالب دوره‌ها
آشنایی با مدل برنامه نویسی TAP
تاریخچه‌ی اعمال غیر همزمان در دات نت فریم ورک

دات نت فریم ورک، از زمان ارائه نگارش یک آن، از اعمال غیرهمزمان و API خاص آن پشتیبانی می‌کرده‌است. همچنین این مورد یکی از ویژگی‌های Win32 نیز می‌باشد. نوشتن کدهای همزمان متداول بسیار ساده است. در این نوع کدها هر عملیات خاص، پس از پایان عملیات قبلی انجام می‌شود.
        public string TestNoneAsync()
        {
            var webClient = new WebClient();
            return webClient.DownloadString("http://www.google.com");
        }
در این مثال متداول، متد DownloadString به صورت همزمان یا synchronous عمل می‌کند. به این معنا که تا پایان عملیات دریافت اطلاعات از وب، منتظر مانده و ترد جاری را قفل می‌کند. مشکل از جایی آغاز می‌شود که مدت زمان دریافت اطلاعات، طولانی باشد. چون این عملیات در ترد UI در حال انجام است، کل رابط کاربری برنامه تا پایان عملیات نیز قفل شده و دیگر پاسخگوی سایر اعمال رسیده نخواهد بود. در این حالت عموما ویندوز در نوار عنوان برنامه، واژه‌های Not responding را نمایش می‌دهد.
این مورد همچنین در برنامه‌های سمت سرور نیز حائز اهمیت است. با قفل شدن تعداد زیادی ترد در حال اجرا، عملا قدرت پاسخ‌دهی سرور نیز کاهش می‌یابد. بنابراین در این نوع موارد، برنامه‌های چند ریسمانی هرچند در سمت کلاینت ممکن است مفید واقع شوند و برای مثال ترد UI را آزاد کنند، اما اثر آنچنانی بر روی برنامه‌های سمت سرور ندارند. زیرا در آن‌ها می‌توان هزاران ترد را ایجاد کرد که همگی دارای کدهای اصطلاحا blocking باشند. برای حل این مساله استفاده از API غیرهمزمان توصیه می‌شود.
برای نمونه کلاس WebClient توکار دات نت، دارای متدی به نام DownloadStringAsync نیز می‌باشد. این متد به محض فراخوانی، ترد جاری را آزاد می‌کند. به این معنا که فراخوانی آن سبب توقف ترد جاری برای دریافت نتیجه‌ی دریافت اطلاعات از وب نمی‌شود. به این نوع API، یک Asynchronous API گفته می‌شود؛ زیرا با سایر کدهای نوشته شده، هماهنگ و همزمان اجرا نمی‌شود.
هر چند این کد جدید مشکل عدم پاسخ دهی برنامه را برطرف می‌کند، اما مشکل دیگری را به همراه دارد؛ چگونه باید حاصل عملیات آن‌را پس از پایان کار دریافت کرد؟ چگونه باید خطاها و مشکلات احتمالی را مدیریت کرد؟
برای مدیریت این مساله، رخدادی به نام DownloadStringCompleted تعریف شده‌است. روال رویدادگردان آن پس از پایان کار دریافت اطلاعات از وب، فراخوانی می‌گردد.
        public void TestAsync()
        {
            var webClient = new WebClient();
            webClient.DownloadStringAsync(new Uri("http://www.google.com"));
            webClient.DownloadStringCompleted += webClientDownloadStringCompleted;
        }

        void webClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // use e.Result
        }
در اینجا همچنین توسط آرگومان DownloadStringCompletedEventArgs، موفقیت یا شکست عملیات نیز گزارش می‌شود و مقدار e.Result حاصل عملیات است.

مشکل!
ما سادگی یک عملیات همزمان را از دست دادیم. متد TestNoneAsync از لحاظ پیاده سازی و همچنین خواندن و نگهداری آن در طول زمان، بسیار ساده‌تر است از نمونه‌ی TestAsync نوشته شده. در کدهای غیرهمزمان فوق، یک متد ساده، به دو متد مجزا خرد شده‌است و نتیجه‌ی نهایی، درون یک روال رخدادگردان بدست می‌آید.
به این مدل، EAP یا Event based asynchronous pattern نیز گفته می‌شود. EAP در دات نت 2 معرفی شد. روال‌های رخدادگردان در این حالت، در ترد اصلی برنامه اجرا می‌شوند. اما اگر به حالت اصلی اعمال غیرهمزمان موجود از دات نت یک کوچ کنیم، اینطور نیست. در WinForms و WPF برای به روز رسانی رابط کاربری نیاز است اطلاعات دریافت شده در همان تردی که رابط کاربری ایجاد شده است، تحویل گرفته شده و استفاده شوند. در غیراینصورت استثنایی صادر شده و برنامه خاتمه می‌یابد.


آشنایی با Synchronization Context

ابتدا یک برنامه‌ی WinForms ساده را آغاز کرده و یک دکمه‌ی جدید را به نام btnGetInfo و یک تکست باکس را به نام txtResults، به آن اضافه کنید. سپس کدهای فرم اصلی آن‌را به نحو ذیل تغییر دهید:
using System;
using System.Linq;
using System.Net;
using System.Windows.Forms;

namespace Async02
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnGetInfo_Click(object sender, EventArgs e)
        {
            var req = (HttpWebRequest)WebRequest.Create("http://www.google.com");
            req.Method = "HEAD";
            req.BeginGetResponse(
                asyncResult =>
                {
                    var resp = (HttpWebResponse)req.EndGetResponse(asyncResult);
                    var headersText = formatHeaders(resp.Headers);
                    txtResults.Text = headersText;
                }, null);
        }

        private string formatHeaders(WebHeaderCollection headers)
        {
            var headerString = headers.Keys.Cast<string>()
                                      .Select(header => string.Format("{0}:{1}", header, headers[header]));
            return string.Join(Environment.NewLine, headerString.ToArray());
        }
    }
}
در اینجا از روش دیگری برای دریافت اطلاعات از وب استفاده کرده‌ایم. با استفاده از امکانات HttpWebRequest، کوئری‌های پیشرفته‌تری را می‌توان تهیه کرد. برای مثال می‌توان نوع متد را به HEAD تنظیم نمود؛ تا صرفا مقادیر هدر آدرس درخواستی از سرور، دریافت شوند.
همچنین در این مثال از متد غیرهمزمان BeginGetResponse نیز استفاده شده‌است. در این نوع API خاص، کار با BeginGetResponse آغاز شده و سپس در callback نهایی توسط EndGetResponse، نتیجه‌ی عملیات به دست می‌آید.
اگر برنامه را اجرا کنید، با استثنای زیر مواجه خواهید شد:
 An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Cross-thread operation not valid: Control 'txtResults' accessed from a thread other than the thread it was created on.
علت اینجا است که asyncResult دریافتی، در تردی دیگر نسبت به ترد اصلی برنامه که UI را اداره می‌کند، اجرا می‌شود. یکی از راه حل‌های این مشکل و انتقال اطلاعات به ترد اصلی برنامه، استفاده از Synchronization Context است:
        private void btnGetInfo_Click(object sender, EventArgs e)
        {
            var sync = SynchronizationContext.Current;
            var req = (HttpWebRequest)WebRequest.Create("http://www.google.com");
            req.Method = "HEAD";
            req.BeginGetResponse(
                asyncResult =>
                {
                    var resp = (HttpWebResponse)req.EndGetResponse(asyncResult);
                    var headersText = formatHeaders(resp.Headers);
                    sync.Post(delegate { txtResults.Text = headersText; }, null);
                }, null);
        }
SynchronizationContext.Current در اینجا چون در ابتدای متد دریافت اطلاعات اجرا می‌شود، به ترد UI، یا ترد اصلی برنامه اشاره می‌کند. به همین جهت این زمینه را نباید داخل Async callback دریافت کرد؛ زیرا ترد جاری آن، ترد UI مدنظر ما نیست. سپس همانطور که ملاحظه می‌کنید، توسط متد Post آن می‌توان اطلاعات را در زمینه‌ی تردی که SynchronizationContext به آن اشاره می‌کند اجرا کرد.


برای درک بهتر آن، سه break point را پیش از متد BeginGetResponse، داخل  Async calback و داخل delegate متد Post قرار دهید. پس از اجرای برنامه، از منوی دیباگ در VS.NET گزینه‌ی Windows و سپس Threads را انتخاب کنید.
در اینجا همانطور که مشخص است، کد داخل delegate تعریف شده، در ترد اصلی برنامه اجرا می‌شود و نه یکی از Worker threadهای ثانویه.
هر چند استفاده از متدهای تو در تو و lambda syntax، نیاز به تعریف چندین متد جداگانه را برطرف کرده‌است، اما باز هم کد ساده‌ای به نظر نمی‌رسد. در سی شارپ 5، برای مدیریت بهتر تمام مشکلات یاد شده، پشتیبانی توکاری از اعمال غیرهمزمان، به هسته‌ی زبان اضافه شده‌است.


Syntax ابتدایی یک متد Async

در ابتدا کلاس و متد Async زیر را در نظر بگیرید:
using System;
using System.Threading.Tasks;

namespace Async01
{
    public class AsyncExample
    {
        public async Task DoWorkAsync(int parameter)
        {
            await Task.Delay(parameter);
            Console.WriteLine(parameter);
        }
    }
}
شیوه‌ی نگارش آن بر اساس راهنمای نوشتن برنامه‌های Async یا Task asynchronous programming model یا به اختصار TAP است:
- در مدل برنامه نویسی TAP، متدهای غیرهمزمان باید یک Task را بازگشت دهند؛ یا نمونه‌ی جنریک آن‌را. البته کامپایلر، async void را نیز پشتیبانی می‌کند ولی در قسمت‌های بعدی بررسی خواهیم کرد که چرا استفاده از آن مشکل‌زا است و باید از آن پرهیز شود.
- همچنین مطابق TAP، اینگونه متدها باید به پسوند Async ختم شوند تا استفاده کننده در حین کار با Intellisense، بتواند آ‌ن‌ها را از متدهای معمولی سریعتر تشخیص دهد.
- از واژه‌ی کلیدی async نیز استفاده می‌گردد تا کامپایلر از وجود اعمال غیر همزمان مطلع گردد.
- await به کامپایلر می‌گوید، عبارت پس از من، یک وظیفه‌ی غیرهمزمان است و ادامه‌ی کدهای نوشته شده، تنها زمانی باید اجرا شوند که عملیات غیرهمزمان معرفی شده، تکمیل گردد.

در متد DoWorkAsync، ابتدا به اندازه‌‌ای مشخص توقف حاصل شده و سپس سطر بعدی یعنی Console.WriteLine اجرا می‌شود.


یک اشتباه عمومی! استفاده از واژه‌های کلیدی async و await متد شما را async نمی‌کنند.

برخلاف تصور ابتدایی از بکارگیری واژه‌های کلیدی async و await، این کلمات نحوه‌ی اجرای متد شما را async نمی‌کنند. این کلمات صرفا برای تشکیل متدهایی که هم اکنون غیرهمزمان هستند، مفید می‌باشند. برای توضیح بیشتر آن به مثال ذیل دقت کنید:
        public async Task<double> GetNumberAsync()
        {
            var generator = new Random();
            await Task.Delay(generator.Next(1000));

            return generator.NextDouble();
        }
در این متد با استفاده از Task.Delay، انجام یک عملیات طولانی شبیه سازی شده‌است؛ مثلا دریافت یک عدد یا نتیجه از یک وب سرویس. سپس در نهایت، عددی را بازگشت داده است. برای بازگشت یک خروجی double، در اینجا از نمونه‌ی جنریک Task استفاده شده‌است.
در ادامه برای استفاده از آن خواهیم داشت:
        public async Task<double> GetSumAsync()
        {
            var leftOperand = await GetNumberAsync();
            var rightOperand = await GetNumberAsync();

            return leftOperand + rightOperand;
        }
خروجی این متد تنها زمانی بازگشت داده می‌شود که نتایج leftOperand و rightOperand از وب سرویس فرضی، دریافت شده باشند و در اختیار مصرف کننده قرارگیرند. بنابراین همانطور که ملاحظه می‌کنید از واژه‌ی کلیدی await جهت تشکیل یک عملیات غیرهمزمان و مدیریت ساده‌تر کدهای نهایی، شبیه به کدهای معمولی همزمان استفاده شده‌است.
در کدهای همزمان متداول، سطر اول ابتدا انجام می‌شود و بعد سطر دوم و الی آخر. با استفاده از واژه‌ی کلیدی await یک چنین عملکردی را با اعمال غیرهمزمان خواهیم داشت. پیش از این برای مدیریت اینگونه اعمال از یک سری callback و یا رخداد استفاده می‌شد. برای مثال ابتدا عملیات همزمانی شروع شده و سپس نتیجه‌ی آن در یک روال رخ‌داد گردان جایی در کدهای برنامه دریافت می‌شد (مانند مثال ابتدای بحث). اکنون تصور کنید که قصد داشتید جمع نهایی حاصل دو عملیات غیرهمزمان را از دو روال رخدادگردان جدا از هم، جمع آوری کرده و بازگشت دهید. هرچند اینکار غیرممکن نیست، اما حاصل کار به طور قطع آنچنان زیبا نبوده و قابلیت نگهداری پایینی دارد. واژه‌ی کلیدی await، انجام اینگونه امور غیرهمزمان را طبیعی و همزمان جلوه می‌دهد. به این ترتیب بهتر می‌توان بر روی منطق و الگوریتم‌های مورد استفاده تمرکز داشت، تا اینکه مدام درگیر مکانیک اعمال غیرهمزمان بود.

امکان استفاده از واژه‌ی کلیدی await در هر جایی از کدها وجود دارد. برای نمونه در مثال زیر، برای ترکیب دو عملیات غیرهمزمان، از await در حین تشکیل عملیات ضرب نهایی، دقیقا در جایی که مقدار متد باید بازگشت داده شود، استفاده شده‌است:
        public async Task<double> GetProductOfSumAsync()
        {
            var leftOperand = GetSumAsync();
            var rightOperand = GetSumAsync();

            return await leftOperand * await rightOperand;
        }
اگر await را از این مثال حذف کنیم، خطای کامپایل زیر را دریافت خواهیم کرد:
 Operator '*' cannot be applied to operands of type 'System.Threading.Tasks.Task<double>' and 'System.Threading.Tasks.Task<double>'
خروجی متد GetSumAsync صرفا یک Task است و نه یک عدد. پس از استفاده از await، عملیات آن انجام شده و بازگشت داده می‌شود.


اگر متد DownloadString همزمان ابتدای بحث را نیز بخواهیم تبدیل به نمونه‌ی async سی‌شارپ 5 کنیم، می‌توان از متد الحاقی جدید آن به نام DownloadStringTaskAsync کمک گرفت:
        public async Task<string> DownloadAsync()
        {
            var webClient = new WebClient();
            return await webClient.DownloadStringTaskAsync("http://www.google.com");
        }
نکته‌ی مهم این کد علاوه بر ساده سازی اعمال غیر همزمان، برای استفاده از نتیجه‌ی نهایی آن، نیازی به SynchronizationContext معرفی شده در تاریخچه‌ی ابتدای بحث نیست. نتیجه‌ی دریافتی از آن در ترد اصلی برنامه تحویل داده شده و به سادگی قابل استفاده است.


سؤال: آیا استفاده از await نیز ترد جاری را قفل می‌کند؟

اگر به کدها دقت کنید، استفاده از await به معنای صبر کردن تا پایان عملیات async است. پس اینطور به نظر می‌رسد که در اینجا نیز ترد اصلی، همانند قبل قفل شده‌است.
        public void TestDownloadAsync()
        {
            Debug.WriteLine("Before DownloadAsync");
            DownloadAsync();
            Debug.WriteLine("After DownloadAsync");
        }
اگر این متد را اجرا کنید (در آن await بکار نرفته)، بلافاصله خروجی ذیل را مشاهده خواهید کرد:
 Before DownloadAsync
After DownloadAsync
به این معنا که در اصل، همانند سایر روش‌های async موجود از دات نت یک، در اینجا نیز فراخوانی متد async ترد اصلی را بلافاصله آزاد می‌کند و ترد آن‌را قفل نخواهد کرد. استفاده از await نیز عملکرد کدها را تغییر نمی‌دهد. تنها کامپایلر در پشت صحنه همان کدهای لازم جهت مدیریت روال‌های رخدادگردان و callbackها را تولید می‌کند، به نحوی که صرفا نحوه‌ی کدنویسی ما همزمان به نظر می‌رسد، اما در پشت صحنه، نحوه‌ی اجرای آن غیرهمزمان است.


برنامه‌های Async و نگارش‌های مختلف دات نت

شاید در ابتدا به نظر برسد که قابلیت‌های جدید async و await صرفا متعلق هستند به دات نت 4.5 به بعد؛ اما خیر. اگر کامپایلری را داشته باشید که از این واژه‌های کلیدی را پشتیبانی کند، امکان استفاده از آن‌ها را با دات نت 4 نیز خواهید داشت. برای این منظور تنها کافی است از VS 2012 به بعد استفاده نمائید. سپس در کنسول پاورشل نیوگت دستور ذیل را اجرا نمائید (فقط برای برنامه‌های دات نت 4 البته):
 PM> Install-Package Microsoft.Bcl.Async
این روال متداول VS.NET بوده است تا به امروز. برای مثال اگر VS 2010 را نصب کنید و سپس یک برنامه‌ی دات نت 3.5 را ایجاد کنید، امکان استفاده‌ی کامل از تمام امکانات سی‌شارپ 4، مانند آرگومان‌های نامدار و یا مقادیر پیش فرض آرگومان‌ها را در یک برنامه‌ی دات نت 3.5 نیز خواهید داشت. همین نکته در مورد async نیز صادق است. VS 2012 (یا نگارش‌های جدیدتر) را نصب کنید و سپس یک پروژه‌ی دات نت 4 را آغاز کنید. امکان استفاده از async و await را خواهید داشت. البته در این حالت دسترسی به متدهای الحاقی جدید را مانند DownloadStringTaskAsync نخواهید داشت. برای رفع این مشکل باید بسته‌ی  Microsoft.Bcl.Async را نیز توسط نیوگت نصب کنید.
اشتراک‌ها
سفارشی کردن ابزارها (NodeJs, Grunt, Bower, Npm, Git, ...) در Visual Studio 2015

All of these tools are shipped as part of Visual Studio but are not installed in the same way as you would install them manually. They are all located in the Visual Studio install directory and are used by some of the new features such as the Task Runner Explorer and the new ASP.NET 5 project system. 

سفارشی کردن ابزارها (NodeJs, Grunt, Bower, Npm, Git, ...) در Visual Studio 2015
مطالب
نحوه‌ی صحیح کار کردن با بوت استرپ
DOM در حالت عادی بسیار نامرتب است. همچنین با افزودن کلاس‌های CSS، کد HTML به مراتب نامرتب‌تر از قبل می‌شود. بوت استرپ نیز شامل تعداد زیادی از کلاس‌های CSS می‌باشد که برای انجام وظایف خاصی به HTML اضافه می‌شوند.

روش متداول استفاده از بوت‌استرپ 

Embedd کردن کلاس‌های CSS بوت‌استرپ به صورت مستقیم درون HTML

 اغلب فریم‌ورک‌ها، از لحاظ معنایی یا semantic، دارای مشکل هستند. اگر به سورس HTML صفحاتی که با این نوع از فریم‌ورک‌ها ساخته شده باشند نگاهی بیندازید با حجم زیادی از کلاس‌هایی مانند <"div class="row> و یا <"div class="col-sm> مواجه خواهید شد. نوشتن کد‌های HTML به این صورت از لحاظ معنایی اشتباه است. مثلاً اگر بنا به دلایلی سازندگان بوت استرپ تصمیم بگیرند نام کلاس‌های را در نسخه بعدی این فریم‌ورک تغییر دهند (مانند تغییر نام کلاس‌ها در نسخه‌ی 3 بوت استرپ)، و یا اگر در آینده بخواهید از یک فریم‌ورک دیگر در سایت‌تان استفاده کنید. باید این تغییرات را در تمام صفحات سایت‌تان اعمال کنید؛ در نتیجه اینکار زمان زیادی را از شما صرف میکند.

راه‌حل؟

استفاده از CSS preprocessors

بوت‌استرپ، از Less برای اینکار استفاده می‌کند. Less در واقع یک CSS preprocessor نوشته شده با جاوا اسکریپت است که قابلیت اجرا در مرورگر را دارد. Less امکانات زیادی، از قبیل استفاده از توابع، متغیرها، Mixins و ... را در اختیار شما قرار می‌دهد. در واقع هدف از Less، نگهداری آسان و قابلیت توسعه فایل‌های CSS می‌باشد. در این حالت شما کدهای CSS خود را درون فایل‌هایی با پسوند Less می‌نویسید. در این حالت بجای پیوست کردن کلاس‌های بوت‌استرپ در کد HTML، آن را درون استایل‌شیت پیوست خواهید کرد. همانطور که عنوان شد، بوت‌استرپ با Less نوشته شده است. فایل‌های Less بوت‌استرپ را می‌توانید از مخرن کد گیت‌هاب آن دانلود نمائید یا اینکه از طریق نیوگت می‌توانید آن را نصب کنید: 
PM> Install-Package Twitter.Bootstrap.Less
کار با Less خیلی ساده است. به عنوان مثال در کد زیر یک کلاس با نام loud داریم که استایل‌هایی را به آن اعمال کرده‌ایم. اکنون جهت استفاده مجدد از این استایل‌ها برای کلاسی دیگر، نیاز به نوشتن مجدد آن نیست. کافی همانند یک تابع در هر کلاسی آن را فراخوانی کنیم:
.loud {
  color: red;
}

// Make all H1 elements loud
h1 {
  .loud;
}

نکته: در Visual Studio 2012 Update 2 به بعد به صورت توکار از فایل‌های Less پشتیبانی می‌شود. (توسط پلاگین Web Essentials)

استفاده از Mixins

با استفاده از Mixins می‌توانیم عناصر داخل صفحات‌مان را به صورت Semantic تعریف نمائیم. به عنوان مثال می‌خواهیم با استفاده از سیستم گرید بوت‌استرپ، ساختاری مانند تصویر زیر را داشته باشیم:

در حالت معمول با استفاده از کلاس‌های CSS بوت‌استرپ می‌توانیم اینکار را انجام دهیم:
<div class="container">
    <div class="row">
        <div class="col-md-8">
            Content - Main
        </div>
        <div class="col-md-4">
            Content - Secondary
        </div>
    </div>
</div>
کد فوق را بهتر است به این صورت بنویسیم: 
<div class="wrapper">
    <div class="content-main">
         Content - Main
    </div>
    <div class="content-secondary">
        Content - Secondary
    </div>
</div>

در بوت استرپ از Less Mixins جهت اعمال استایل هایی مانند row و column می‌توانیم استفاده کنیم. به طور مثال برای اعمال استایل به کلاس‌های فوق می‌توانیم به این صورت عمل کنیم:
// Core variables and mixins
@import "variables.less";
@import "mixins.less";
.wrapper {
  .make-row();
}
.content-main {
  .make-lg-column(8);
}
.content-secondary {
  .make-lg-column(3);
  .make-lg-column-offset(1);
}
کد فوق بعد از کامپایل به کد زیر تبدیل خواهد شد:
.wrapper {
  margin-left: -15px;
  margin-right: -15px;
}
.content-main {
  position: relative;
  min-height: 1px;
  padding-left: 15px;
  padding-right: 15px;
}
@media (min-width: 1200px) {
  .content-main {
    float: left;
    width: 66.66666666666666%;
  }
}
.content-secondary {
  position: relative;
  min-height: 1px;
  padding-left: 15px;
  padding-right: 15px;
}
@media (min-width: 1200px) {
  .content-secondary {
    float: left;
    width: 25%;
  }
}
@media (min-width: 1200px) {
  .content-secondary {
    margin-left: 8.333333333333332%;
  }
}
همانطور که قبلاً عنوان شد ویژوال استودیو به راحتی توسط افزونه Web Essentials از فایل‌های Less پشتیبانی می‌کند. در نتیجه کامپایل فایل‌های Less داخل ویژوال استودیو توسط این افزونه به راحتی قابل انجام می‌باشد. 
یک قابلیت جالب دیگر در رابطه با فایل‌هایی Less، تولید نسخه‌های CSS عادی و فشرده نهایی توسط افزونه Web Essentials می‌باشد. به طور مثال شما می‌توانید نسخه minified شده را به Layout تان اضافه کنید. بعد از هربار تغییر در فایل Less این فایل نیز به روز خواهد شد:

 همچنین برای دیگر اجزای بوت‌استرپ نیز می‌توانید به این صورت عمل کنید:  
<!-- Before -->
<a href="#" class="btn danger large">Click me!</a>

<!-- After -->
<a href="#" class="annoying">Click me!</a>

a.annoying {
  .btn;
  .btn-danger;
  .btn-large;
}

خب، با استفاده از این حالت، کد‌های HTML به‌صورت مرتب‌تر، قابل‌انعطاف‌تر و همچنین از لحاظ معنایی(Semantic) استاندارد خواهند بود. بنابراین با آمدن یک فریم‌ورک جدید، به راحتی امکان سوئیچ‌کردن برای ما میسر و آسان‌تر از قبل خواهد شد.