نه. سبکتر با توجه به وضعیت دسترسی به اینترنت پرسرعت و هزینه آن. مثل همین سری MVC یا EF در سایت. ارائه متن دروس؛ به همراه پشتیبانی برای افراد شرکت کننده. قابل ارائه توسط تمام نویسندگان سایت.
در قسمت اول این سری، با مدل برنامه نویسی Event based asynchronous pattern ارائه شده از دات نت 2 و همچنین APM یا Asynchronous programming model موجود از نگارش یک دات نت، آشنا شدیم (به آن الگوی IAsyncResult هم گفته میشود). نکتهی مهم این الگوها، استفادهی گسترده از آنها در کدهای کلاسهای مختلف دات نت فریم ورک است و برای بسیاری از آنها هنوز async API سازگار با نگارش مبتنی بر Taskهای سیشارپ 5 ارائه نشدهاست. هرچند دات نت 4.5 سعی کردهاست این خلاء را پوشش دهد، برای مثال متد الحاقی DownloadStringTaskAsync را به کلاس WebClient اضافه کردهاست و امثال آن، اما هنوز بسیاری از کلاسهای دیگر دات نتی هستند که معادل Task based API ایی برای آنها طراحی نشدهاست. در ادامه قصد داریم بررسی کنیم چگونه میتوان این الگوهای مختلف قدیمی برنامه نویسی غیرهمزمان را با استفاده از روشهای جدیدتر ارائه شده بکار برد.
نگاشت APM به یک Task
در قسمت اول، نمونه مثالی را از APM، که در آن کار با BeginGetResponse آغاز شده و سپس در callback نهایی توسط EndGetResponse، نتیجهی عملیات به دست میآید، مشاهده کردید. در ادامه میخواهیم یک محصور کنندهی جدید را برای این نوع API قدیمی تهیه کنیم، تا آنرا به صورت یک Task ارائه دهد.
همانطور که در این مثال مشاهده میکنید، یک چنین سناریوهایی در TPL یا کتابخانهی Task parallel library پیش بینی شدهاند. در اینجا یک محصور کننده برای متدهای BeginRead و EndRead کلاس Stream دات نت ارائه شدهاست. به عمد نیز به صورت یک متد الحاقی تهیه شدهاست تا در حین استفاده از آن اینطور به نظر برسد که واقعا کلاس Stream دارای یک چنین متد Async ایی است. مابقی کار توسط متد Task.Factory.FromAsync انجام میشود. متد FromAsync دارای امضاهای متعددی است تا اکثر حالات APM را پوشش دهد.
در مثال فوق BeginRead و EndRead استفاده شده از نوع delegate هستند. چون خروجی EndRead از نوع int است، خروجی متد نیز از نوع Task of int تعیین شدهاست. همچنین سه پارامتر ابتدایی BeginRead ، دقیقا data، offset و count هستند. دو پارامتر آخر آن callback و state نام دارند. پارامتر callback توسط متد FromAsync فراهم میشود و state نیز در اینجا null درنظر گرفته شدهاست.
یک مثال استفاده از آنرا در ادامه مشاهده میکنید:
File.OpenRead، خروجی از نوع استریم دارد. سپس متد الحاقی ReadAsync بر روی آن فراخوانی شدهاست و نهایتا تعداد بایت خوانده شده نمایش داده میشود.
البته همانطور که پیشتر نیز عنوان شد، استفاده از خاصیت Result، اجرای کد را بجای غیرهمزمان بودن، به حالت همزمان تبدیل میکند.
در اینجا چون خروجی متد ReadAsync یک Task است، میتوان از متد ContinueWith نیز بر روی آن جهت دریافت نتیجه استفاده کرد:
یک نکته
پروژهی سورس بازی به نام Async Generator در GitHub، سعی کردهاست برای ساده سازی نوشتن محصور کنندههای مبتنی بر Task روش APM، یک Code generator تولید کند. فایلهای آنرا از آدرس ذیل میتوانید دریافت کنید:
نگاشت EAP به یک Task
نمونهای از Event based asynchronous pattern یا EAP را در قسمت اول، زمانیکه روال رخدادگردان webClient.DownloadStringCompleted را بررسی کردیم، مشاهده نمودید. کار کردن با آن نسبت به APM بسیار سادهتر است و نتیجهی نهایی عملیات غیرهمزمان را در یک روال رخدادگران، در اختیار استفاده کننده قرار میدهد. همچنین در روش EAP، اطلاعات در همان Synchronization Context ایی که عملیات شروع شدهاست، بازگشت داده میشود. به این ترتیب اگر آغاز کار در ترد UI باشد، نتیجه نیز در همان ترد دریافت خواهد شد. به این ترتیب دیگر نگران دسترسی به مقدار آن در کارهای UI نخواهیم بود؛ اما در APM چنین ضمانتی وجود ندارد.
متاسفانه TPL همانند روش FromAsync معرفی شده در ابتدای بحث، راه حل توکاری را برای محصور سازی متدهای روش EAP ارائه ندادهاست. اما با استفاده از امکانات TaskCompletionSource آن میتوان چنین کاری را انجام داد. در ادامه سعی خواهیم کرد همان متد الحاقی توکار DownloadStringTaskAsync ارائه شده در دات نت 4.5 را از صفر بازنویسی کنیم.
روش انجام کار را در اینجا ملاحظه میکنید. ابتدا باید تعاریف delaget مرتبط با رخدادگردان Completed اضافه شوند. یکبار += را ملاحظه میکنید و بار دوم -= را. مورد دوم جهت آزاد سازی منابع و جلوگیری از نشتی حافظهی روال رخدادگردان هنوز متصل، ضروری است.
سپس از TaskCompletionSource برای تبدیل این عملیات به یک Task کمک میگیریم. اگر args.Cancelled مساوی true باشد، یعنی عملیات دریافت فایل لغو شدهاست. بنابراین متد SetCanceled منبع Task ایجاد شده را فراخوانی خواهیم کرد. این مورد استثنایی را در کدهای فراخوان سبب میشود. به همین دلیل بررسی خطا با یک if else پس از آن انجام شدهاست. برای بازگشت خطای دریافت شده از متد SetException و برای بازگشت نتیجهی واقعی دریافتی، از متد SetResult میتوان استفاده کرد.
به این ترتیب متد الحاقی غیرهمزمان جدیدی را به نام DownloadTextTaskAsync برای محصور سازی متد EAP ایی به نام DownloadStringAsync و همچنین رخدادگران آن تهیه کردیم.
نگاشت APM به یک Task
در قسمت اول، نمونه مثالی را از APM، که در آن کار با BeginGetResponse آغاز شده و سپس در callback نهایی توسط EndGetResponse، نتیجهی عملیات به دست میآید، مشاهده کردید. در ادامه میخواهیم یک محصور کنندهی جدید را برای این نوع API قدیمی تهیه کنیم، تا آنرا به صورت یک Task ارائه دهد.
public static class ApmWrapper { public static Task<int> ReadAsync(this Stream stream, byte[] data, int offset, int count) { return Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, data, offset, count, null); } }
در مثال فوق BeginRead و EndRead استفاده شده از نوع delegate هستند. چون خروجی EndRead از نوع int است، خروجی متد نیز از نوع Task of int تعیین شدهاست. همچنین سه پارامتر ابتدایی BeginRead ، دقیقا data، offset و count هستند. دو پارامتر آخر آن callback و state نام دارند. پارامتر callback توسط متد FromAsync فراهم میشود و state نیز در اینجا null درنظر گرفته شدهاست.
یک مثال استفاده از آنرا در ادامه مشاهده میکنید:
using System; using System.IO; using System.Threading.Tasks; namespace Async06 { public static class ApmWrapper { public static Task<int> ReadAsync(this Stream stream, byte[] data, int offset, int count) { return Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, data, offset, count, null); } } class Program { static void Main(string[] args) { using (var stream = File.OpenRead(@"..\..\program.cs")) { var data = new byte[10000]; var task = stream.ReadAsync(data, 0, data.Length); Console.WriteLine("Read bytes: {0}", task.Result); } } } }
البته همانطور که پیشتر نیز عنوان شد، استفاده از خاصیت Result، اجرای کد را بجای غیرهمزمان بودن، به حالت همزمان تبدیل میکند.
در اینجا چون خروجی متد ReadAsync یک Task است، میتوان از متد ContinueWith نیز بر روی آن جهت دریافت نتیجه استفاده کرد:
using (var stream = File.OpenRead(@"..\..\program.cs")) { var data = new byte[10000]; var task = stream.ReadAsync(data, 0, data.Length); task.ContinueWith(t => Console.WriteLine("Read bytes: {0}", t.Result)).Wait(); }
یک نکته
پروژهی سورس بازی به نام Async Generator در GitHub، سعی کردهاست برای ساده سازی نوشتن محصور کنندههای مبتنی بر Task روش APM، یک Code generator تولید کند. فایلهای آنرا از آدرس ذیل میتوانید دریافت کنید:
نگاشت EAP به یک Task
نمونهای از Event based asynchronous pattern یا EAP را در قسمت اول، زمانیکه روال رخدادگردان webClient.DownloadStringCompleted را بررسی کردیم، مشاهده نمودید. کار کردن با آن نسبت به APM بسیار سادهتر است و نتیجهی نهایی عملیات غیرهمزمان را در یک روال رخدادگران، در اختیار استفاده کننده قرار میدهد. همچنین در روش EAP، اطلاعات در همان Synchronization Context ایی که عملیات شروع شدهاست، بازگشت داده میشود. به این ترتیب اگر آغاز کار در ترد UI باشد، نتیجه نیز در همان ترد دریافت خواهد شد. به این ترتیب دیگر نگران دسترسی به مقدار آن در کارهای UI نخواهیم بود؛ اما در APM چنین ضمانتی وجود ندارد.
متاسفانه TPL همانند روش FromAsync معرفی شده در ابتدای بحث، راه حل توکاری را برای محصور سازی متدهای روش EAP ارائه ندادهاست. اما با استفاده از امکانات TaskCompletionSource آن میتوان چنین کاری را انجام داد. در ادامه سعی خواهیم کرد همان متد الحاقی توکار DownloadStringTaskAsync ارائه شده در دات نت 4.5 را از صفر بازنویسی کنیم.
public static class WebClientExtensions { public static Task<string> DownloadTextTaskAsync(this WebClient web, string url) { var tcs = new TaskCompletionSource<string>(); DownloadStringCompletedEventHandler handler = null; handler = (sender, args) => { web.DownloadStringCompleted -= handler; if (args.Cancelled) { tcs.SetCanceled(); } else if(args.Error!=null) { tcs.SetException(args.Error); } else { tcs.SetResult(args.Result); } }; web.DownloadStringCompleted += handler; web.DownloadStringAsync(new Uri(url)); return tcs.Task; } }
سپس از TaskCompletionSource برای تبدیل این عملیات به یک Task کمک میگیریم. اگر args.Cancelled مساوی true باشد، یعنی عملیات دریافت فایل لغو شدهاست. بنابراین متد SetCanceled منبع Task ایجاد شده را فراخوانی خواهیم کرد. این مورد استثنایی را در کدهای فراخوان سبب میشود. به همین دلیل بررسی خطا با یک if else پس از آن انجام شدهاست. برای بازگشت خطای دریافت شده از متد SetException و برای بازگشت نتیجهی واقعی دریافتی، از متد SetResult میتوان استفاده کرد.
به این ترتیب متد الحاقی غیرهمزمان جدیدی را به نام DownloadTextTaskAsync برای محصور سازی متد EAP ایی به نام DownloadStringAsync و همچنین رخدادگران آن تهیه کردیم.
اولین کاری را که میتوان پس از نصب Angular CLI انجام داد، ایجاد یک برنامهی جدید است و نمونهای از آنرا در قسمت قبل بررسی کردیم. در ادامه میخواهیم به پارامترهای بیشتر مرتبط با آن و همچنین نحوهی سفارشی سازی ایجاد برنامههای جدید بپردازیم.
ایجاد برنامههای جدید توسط Angular CLI
دستور خط فرمان ابتدایی ایجاد یک برنامهی جدید توسط Angular CLI به صورت ذیل است
در اینجا ng همان Angular CLI است. new عملی است که قرار است رخ دهد و my-app یک نام دلخواه میباشد.
پس از اجرای این دستور، برنامهی جدید ایجاد شده، در پوشهی جدید my-app قرار میگیرد.
گزینهی دیگر این دستور، استفاده از پرچم dry-run است:
کار این پرچم صرفا گزارش دادن جزئیات عملیات ng new است؛ بدون اینکه فایلی را تولید کند. به این ترتیب میتوان برآوردی را از فایلهای تولید شدهی توسط فرامین ng، پیش از تولید واقعی آنها، مشاهده کرد. برای مثال:
همانطور که مشاهده میکنید، در ابتدای کار پیامی را مبنی بر عدم نوشته شدن این فایلها بر روی فایل سیستم، ارائه دادهاست.
گزینهی دیگر دستور ng new را که در قسمت قبل ملاحظه کردید:
کار پرچم skip-install عدم فراخوانی خودکار دستور npm install است که سبب خواهد شد، برنامه بدون نصب وابستگیهای npm آن، با سرعت هرچه بیشتر، ایجاد شود. از این گزینه میتوان جهت مشاهدهی ساختار فایلهای تولیدی استفاده کرد و در نهایت در این حالت باید دستور npm install را به صورت دستی فراخوانی کرد. پرچم dry-run نیز skip-install را به صورت ضمنی به همراه دارد.
برای مشاهدهی سایر پرچمهای مرتبط با دستور ng new میتوان از پرچم help استفاده کرد:
بررسی فایل angular-cli.json.
فایل angular-cli.json. حاوی تنظیمات Angular CLI است.
در ابتدای این فایل، نام برنامهی جدید را مشاهده میکنید. این نام، همانی است که توسط دستور ng new my-app تعیین گردید.
سپس محل پوشهی source برنامه و همچنین خروجی نهایی آن، مشخص میشوند:
یکی از تنظیمات مهم این فایل، مقدار prefix است:
از این مقدار برای تنظیم مقدار prefix تمام کامپوننتها و دایرکتیوهای تولیدی توسط Angular CLI استفاده میشود. برای مثال اگر به فایل src\app\app.component.ts دقت کنید:
نام selector آن با app- شروع شدهاست که این app، از مقدار prefix فایل angular-cli.json. دریافت شدهاست.
تغییر این مقدار صرفا بر روی کامپوننتهای جدید تولید شدهی توسط Angular CLI تاثیرگذار خواهند بود. اگر میخواهید در ابتدای کار تولید یک برنامه، این مقدار را مشخص کنید، میتوان از پرچم prefix استفاده کرد و در صورت عدم ذکر آن، مقدار پیش فرض app برای آن درنظر گرفته میشود:
عدم ایجاد مخزن Git به همراه ng new
با صدور فرمان ng new، کار ایجاد یک مخزن Git نیز به صورت خودکار انجام خواهد شد. برای نمونه اگر خواستید برنامهای را بدون نصب وابستگیها، بدون ایجاد تستها و بدون ایجاد مخزن git آن تولید کنید، میتوان از دستور ذیل استفاده کرد:
استفادهی از sass بجای css توسط Angular CLI
سیستم Build همراه با Angular CLI مبتنی بر webpack است و به خوبی قابلیت پردازش فایلهای sass را نیز دارا است. اگر خواستید حالت پیش فرض تولید فایلهای css این ابزار را که در فایل angular-cli.json. نمونهای از آن ذکر شدهاست، به همراه فایلهایی مانند app.component.css، به sass تغییر دهید:
تنها کافی است پرچم style را با sass مقدار دهی کرد که حالت پیش فرض آن css است:
با ذکر این پرچم، تغییرات فایل angular-cli.json به صورت ذیل خواهند بود:
و حتی کامپوننت src\app\app.component.ts نیز به همراه شیوهنامهای از نوع sass که در حین ارائه نهایی توسط webpack به صورت خودکار پردازش میشود (بدون نیاز به تنظیمات اضافهتری)، مقدار دهی شدهاست:
و یا حتی اگر علاقمند به استفادهی از less باشید نیز میتوان پرچم style less-- را استفاده نمود.
انجام تنظیمات مسیریابی پیش فرض پروژه جدید توسط Angular CLI
حالت پیش فرض تولید برنامههای جدید Angular CLI به همراه تنظیمات مسیریابی آن نیست. اگر علاقمند هستید تا مبحث مسیریابی را خلاصه کرده و به سرعت تنظیمات ابتدایی مسیریابی را توسط این ابزار تولید کنید، میتوان پرچم routing را نیز در اینجا ذکر کرد:
در این حالت اگر به پوشهی src\app مراجعه کنید، فایل جدید app-routing.module.ts را نیز مشاهده خواهید کرد که AppRoutingModule پیش فرضی در آن تنظیم شدهاست و آمادهی استفاده میباشد.
همچنین فایل app.module.ts را نیز اندکی تغییر داده و این AppRoutingModule جدید را نیز به آن معرفی کردهاست.
به این ترتیب ارتباطات ابتدایی مورد نیاز سیستم مسیریابی برقرار شده و قابل استفادهاست. بنابراین ذکر پرچم routing میتواند یکی از پرچمهای اصلی ایجاد برنامههای جدید مبتنی بر Angular CLI باشد.
اجرای ابتدایی یک برنامهی مبتنی بر Angular CLI
پس از انتخاب پرچمهای مناسب جهت ایجاد یک پروژهی جدید مبتنی بر Angular CLI و همچنین نصب وابستگیهای آنها و یا عدم ذکر پرچم skip-install، اکنون نوبت به اجرای این پروژهاست. به همین جهت از طریق خط فرمان به ریشهی پوشهی برنامهی جدید ایجاد شده، وارد شوید. سپس دستور ذیل را صادر کنید:
در اینجا o- به معنای open است؛ یا گشودن آن در یک مرورگر. به این ترتیب کار کامپایل برنامه صورت گرفته و توسط مرورگر پیشفرض سیستم به صورت خودکار باز خواهد شد. آدرس پیش فرض آن نیز به صورت ذیل است:
نکتهی جالب این وب سرور در این است که تغییرات شما را به صورت خودکار دنبال کرده و بلافاصله ارائه میدهد. برای مثال فایل src\app\app.component.html را گشوده و به صورت ذیل تغییر دهید:
پس از ذخیرهی آن، بلافاصله خروجی نهایی را در مرورگر خواهید دید.
تغییر پیش فرضهای عمومی Angular CLI
تا اینجا مشاهده کردیم که اگر بخواهیم مقدار prefix پیش فرض را که به app تنظیم شدهاست به myCompany تغییر دهیم، یا میتوان از پرچم prefix در ابتدای کار فراخوانی دستور ng new استفاده کرد و یا میتوان فایل angular-cli.json. را نیز دستی ویرایش نمود. برای تغییر عمومی و سراسری مقدار پیش فرض app میتوان از دستور ng set استفاده کرد:
دستور اول فایل angular-cli.json. پروژهی جاری را ویرایش میکند و دستور دوم، فایل angular-cli.json سراسری Angular CLI را مقدار دهی خواهد کرد (با توجه به سوئیچ g- آن). البته بدیهی است ویرایشگرهای امروزی به خوبی قابلیت ویرایش فایلهای json را ارائه میدهند و شاید نیازی به استفادهی از این دستورات،حداقل برای اعمال تغییرات محلی نباشد.
و یا اگر بخواهید نوع شیوهنامهی مورد استفاده را ویرایش کنید، میتوان از یکی از دو دستور ذیل استفاده کرد (اولی محلی است و دومی عمومی):
اجرای امکانات Linting پروژههای مبتنی بر Angular CLI
برای بررسی کیفیت کدهای نوشته شده، میتوان از امکانات Linting استفاده کرد. برای این منظور تنها کافی است دستور ذیل را در ریشهی پروژه اجرا نمود:
برای مشاهدهی تمام گزینههای آن دستور زیر را صادر کنید:
اگر علاقمند هستید تا خروجی این ابزار، با رنگهای بهتری نمایش داده شوند، میتوان از دستور ذیل استفاده کرد:
به علاوه این ابزار قابلیت رفع مشکلات را با اعمال تغییراتی در کدهای موجود نیز به همراه دارد:
برای این منظور میتوان از پرچم fix آن استفاده کرد.
یک مثال: فایل src\app\app.component.ts را باز کنید و به عمد تعدادی مشکل را در آن ایجاد نمائید. برای نمونه دو سطر ابتدایی آنرا به صورت ذیل تغییر دهید:
اکنون اگر ng lint را اجرا کنیم، به خروجی ذیل خواهیم رسید:
عنوان میکند که بهتر است number به صورت یک const تعریف شود و همچنین یک سمیکالن در سطر اول فراموش شدهاست.
اکنون اگر دستور ng lint --fix را فراخوانی کنیم، تغییرات ذیل به فایل src\app\app.component.ts اعمال خواهند شد:
به صورت خودکار، به سطر اول، یک سمیکالن را اضافه کرده و همچنین سطر دوم را نیز به const تبدیل کردهاست.
ایجاد برنامههای جدید توسط Angular CLI
دستور خط فرمان ابتدایی ایجاد یک برنامهی جدید توسط Angular CLI به صورت ذیل است
> ng new my-app
پس از اجرای این دستور، برنامهی جدید ایجاد شده، در پوشهی جدید my-app قرار میگیرد.
گزینهی دیگر این دستور، استفاده از پرچم dry-run است:
> ng new my-app --dry-run
>ng new my-app --dry-run installing ng You specified the dry-run flag, so no changes will be written. create .editorconfig create README.md create src\app\app.component.css create src\app\app.component.html . . . Project 'my-app' successfully created.
گزینهی دیگر دستور ng new را که در قسمت قبل ملاحظه کردید:
> ng new my-app --skip-install
برای مشاهدهی سایر پرچمهای مرتبط با دستور ng new میتوان از پرچم help استفاده کرد:
> ng new --help
بررسی فایل angular-cli.json.
فایل angular-cli.json. حاوی تنظیمات Angular CLI است.
در ابتدای این فایل، نام برنامهی جدید را مشاهده میکنید. این نام، همانی است که توسط دستور ng new my-app تعیین گردید.
"project": { "name": "my-app" },
"apps": [ { "root": "src", "outDir": "dist",
یکی از تنظیمات مهم این فایل، مقدار prefix است:
"prefix": "app",
@Component({ selector: 'app-root',
تغییر این مقدار صرفا بر روی کامپوننتهای جدید تولید شدهی توسط Angular CLI تاثیرگذار خواهند بود. اگر میخواهید در ابتدای کار تولید یک برنامه، این مقدار را مشخص کنید، میتوان از پرچم prefix استفاده کرد و در صورت عدم ذکر آن، مقدار پیش فرض app برای آن درنظر گرفته میشود:
> ng new my-project --skip-install --prefix myCompany
عدم ایجاد مخزن Git به همراه ng new
با صدور فرمان ng new، کار ایجاد یک مخزن Git نیز به صورت خودکار انجام خواهد شد. برای نمونه اگر خواستید برنامهای را بدون نصب وابستگیها، بدون ایجاد تستها و بدون ایجاد مخزن git آن تولید کنید، میتوان از دستور ذیل استفاده کرد:
> ng new my-project --skip-install --skip-git --skip-tests --skip-commit
استفادهی از sass بجای css توسط Angular CLI
سیستم Build همراه با Angular CLI مبتنی بر webpack است و به خوبی قابلیت پردازش فایلهای sass را نیز دارا است. اگر خواستید حالت پیش فرض تولید فایلهای css این ابزار را که در فایل angular-cli.json. نمونهای از آن ذکر شدهاست، به همراه فایلهایی مانند app.component.css، به sass تغییر دهید:
"styles": [ "styles.css" ], "defaults": { "styleExt": "css", "component": {} }
> ng new my-project --skip-install --style sass
"styles": [ "styles.sass" ], "defaults": { "styleExt": "sass", "component": {} }
@Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.sass'] })
انجام تنظیمات مسیریابی پیش فرض پروژه جدید توسط Angular CLI
حالت پیش فرض تولید برنامههای جدید Angular CLI به همراه تنظیمات مسیریابی آن نیست. اگر علاقمند هستید تا مبحث مسیریابی را خلاصه کرده و به سرعت تنظیمات ابتدایی مسیریابی را توسط این ابزار تولید کنید، میتوان پرچم routing را نیز در اینجا ذکر کرد:
> ng new my-project --skip-install --routing
const routes: Routes = [ { path: '', children: [] } ];
imports: [ BrowserModule, FormsModule, HttpModule, AppRoutingModule ],
اجرای ابتدایی یک برنامهی مبتنی بر Angular CLI
پس از انتخاب پرچمهای مناسب جهت ایجاد یک پروژهی جدید مبتنی بر Angular CLI و همچنین نصب وابستگیهای آنها و یا عدم ذکر پرچم skip-install، اکنون نوبت به اجرای این پروژهاست. به همین جهت از طریق خط فرمان به ریشهی پوشهی برنامهی جدید ایجاد شده، وارد شوید. سپس دستور ذیل را صادر کنید:
>ng serve -o
http://localhost:4200/
نکتهی جالب این وب سرور در این است که تغییرات شما را به صورت خودکار دنبال کرده و بلافاصله ارائه میدهد. برای مثال فایل src\app\app.component.html را گشوده و به صورت ذیل تغییر دهید:
<h1> Test {{title}} </h1>
تغییر پیش فرضهای عمومی Angular CLI
تا اینجا مشاهده کردیم که اگر بخواهیم مقدار prefix پیش فرض را که به app تنظیم شدهاست به myCompany تغییر دهیم، یا میتوان از پرچم prefix در ابتدای کار فراخوانی دستور ng new استفاده کرد و یا میتوان فایل angular-cli.json. را نیز دستی ویرایش نمود. برای تغییر عمومی و سراسری مقدار پیش فرض app میتوان از دستور ng set استفاده کرد:
>ng set apps.prefix myCompany >ng set apps.prefix myCompany -g
و یا اگر بخواهید نوع شیوهنامهی مورد استفاده را ویرایش کنید، میتوان از یکی از دو دستور ذیل استفاده کرد (اولی محلی است و دومی عمومی):
>ng set defaults.styleExt sass >ng set defaults.styleExt sass -g
اجرای امکانات Linting پروژههای مبتنی بر Angular CLI
برای بررسی کیفیت کدهای نوشته شده، میتوان از امکانات Linting استفاده کرد. برای این منظور تنها کافی است دستور ذیل را در ریشهی پروژه اجرا نمود:
> ng lint
> ng lint --help
> ng lint --format stylish
>ng lint --fix
یک مثال: فایل src\app\app.component.ts را باز کنید و به عمد تعدادی مشکل را در آن ایجاد نمائید. برای نمونه دو سطر ابتدایی آنرا به صورت ذیل تغییر دهید:
import { Component } from '@angular/core' let number = 10;
>ng lint --format stylish /src/app/app.component.ts[3, 5]: Identifier 'number' is never reassigned; use 'const' instead of 'let'. /src/app/app.component.ts[1, 42]: Missing semicolon Lint errors found in the listed files.
اکنون اگر دستور ng lint --fix را فراخوانی کنیم، تغییرات ذیل به فایل src\app\app.component.ts اعمال خواهند شد:
import { Component } from '@angular/core'; const number = 10;
پیشنیازها
- «با HttpHandler بیشتر آشنا شوید»
یکی از بزرگترین تغییرات ASP.NET Core نسبت به نگارشهای قبلی آن، مدیریت HTTP pipeline آن است. به عنوان یک توسعه دهندهی ASP.NET به طور قطع با مفاهیمی مانند HttpHandler و HttpModules آشنایی دارید و ... هر دوی اینها با نگارش جدید ASP.NET حذف و با مفهوم جدیدی به نام Middleware جایگزین شدهاند.
- HttpHandlerها عموما مبتنی بر پسوندهای فایلها عمل میکنند. برای نمونه، رایجترین آنها ASP.NET page handler است که هرگاه درخواستی، ختم شدهی به پسوند aspx، به موتور ASP.NET Web forms وارد شد، به این page handler، جهت پردازش نهایی هدایت میشود. محل تنظیم آنها نیز در فایل web.config است.
- HttpModuleها مبتنی بر رخدادها عمل میکنند. HttpModuleها به عنوان جزئی از request pipeline عمل کرده و دسترسی کاملی دارند به رخدادهای طول عمر درخواست رسیده. آنها را میتوان توسط فایلهای global.asax و یا web.config تنظیم کرد.
- MiddleWareها را که جزئی از طراحی OWIN نیز هستند، میتوان به عنوان کامپوننتهای کوچکی از برنامه که قابلیت یکپارچه شدن با HTTP request pipeline را دارند، درنظر گرفت. عملکرد آنها ترکیبی است از هر دوی HttpHandler و HttpModuleها که در نگارشهای قبلی ASP.NET مورد استفاده بودند. از MiddleWareها میتوان برای پیاده سازی اعمال مختلفی جهت پردازش درخواستهای رسیده مانند اعتبارسنجی، کار با سشنها، ثبت وقایع سیستم، مسیریابی و غیره استفاده کرد. OWIN یا Open Web Interface for .NET به توسعه دهندهها امکان غیروابسته کردن برنامهی ASP.NET خود را از وب سرور مورد استفاده میدهد. به علاوه OWIN امکان پلاگین نویسی اجزای مختلف برنامه را بدون وابسته کردن آنها به یکدیگر فراهم میکند. برای مثال میتوان یک پلاگین logger را تهیه کرد تا اطلاعات مختلفی را از درخواستهای رسیده ثبت کند.
مواردی که با ارائهی ASP.NET Core 1.0 حذف شدهاند
- System.Web: یکی از اهداف اصلی OWIN، مستقل کردن برنامهی وب، از هاست آن است تا بتوان از وب سرورهای بیشتری استفاده کرد. System.Web انحصارا برای IIS طراحی شده بود و در این نگارش دیگر وجود خارجی ندارد.
- HttpModules: با Middlewareها جایگزین شدهاند.
- HttpHandlers: البته HttpHandlers از زمان ارائهی اولین نگارش ASP.NET MVC، در چندین سال قبل منسوخ شدند. زیرا پردازش صفحات وب در ASP.NET MVC برخلاف وب فرمها، از فایلهایی با پسوندهای خاص شروع نمیشوند و نقطهی آغازین آنها، اکشن متدهای کنترلرها است.
- فایل global.asax: با حذف شدن HttpModules، دیگر ضرورتی به وجود این فایل نیست.
شباهتهای بینMiddleware و HttpModuleها
همانند HttpModuleها، Middlewareها نیز به ازای هر درخواست رسیده اجرا میشوند و از هر دو میتوان جهت تولید response ایی خاص استفاده کرد.
تفاوتهای بینMiddleware و HttpModuleها
- عموما HttpModule از طریق web.config و یا فایل global.asax تنظیم میشوند. اما Middlewareها تنها از طریق کد و در فایل Startup.cs برنامههای ASP.NET Core 1.0 قابل معرفی هستند.
- به عنوان یک توسعه دهنده، کنترلی را بر روی ترتیب اجرای انواع و اقسام HttpModuleها نداریم. این مشکل در Middlewareها برطرف شده و اکنون ترتیب اجرای آنها، دقیقا مطابق ترتیب افزوده شدن و تعریف آنها در فایل Startup.cs است.
- ترتیب اجرای HttpModuleها هرچه که باشد، برای حالتهای request و response یکی است. اما ترتیب اجرای Middlewareهای مخصوص response، عکس ترتیب اجرای Middlewareهای مخصوص request هستند (تصویر ذیل).
- HttpModuleها را صرفا جهت اتصال کدهایی به رخدادهای طول عمر برنامه میتوان طراحی کرد؛ اما Middleware مستقل هستند از این رخدادها.
- HttpModuleها به System.Web وابستهاند؛ برخلاف Middlewareها که وابستگی به هاست خود ندارند.
Middlewareهای توکار ASP.NET Core 1.0
ASP.NET Core 1.0 به همراه تعدادی Middleware توکار است؛ مانند:
- Authentication: جهت پشتیبانی از اعتبارسنجی
- CORS: برای فعال سازی Cross-Origin Resource Sharing
- Routing: جهت تعریف و محدود سازی مسیریابیهای برنامه
- Session: برای پشتیبانی و مدیریت سشنهای کاربران
- Diagnostics: پشتیبانی از صفحات خطا و اطلاعات زمان اجرای برنامه
و مواردی دیگر که برای توزیع فایلهای استاتیک و یا مرور محتویات پوشهها و امثال آنها طراحی شدهاند که در طی این مطلب و همچنین مطالب آتی، آنها را بررسی خواهیم کرد.
یک مثال: اگر یک پروژهی خالی ASP.NET Core 1.0 را در ویژوال استودیو ایجاد کنید، این پروژه قادر نیست حتی فایلهای استاتیک مانند تصاویر، فایلهای پیش فرض مانند index.html و یا قابلیت مرور سادهی فایلهای موجود در یک پوشه را ارائه دهد (حتی اگر به آنها نیازی نداشته باشید).
طراحی این نگارش از ASP.NET، مبتنی است بر سبک وزن بودن و ماژولار بودن. هر قابلیتی را که نیاز دارید، باید middleware آنرا نیز خودتان به صورت صریح اضافه کنید و یا در غیر اینصورت فعال نیست. برخلاف نگارشهای قبلی ASP.NET که HTTP Moduleهای از سشن گرفته تا اعتبارسنجی و غیره، همگی به صورت پیش فرض در فایل Web.Config فعال بودند؛ مگر اینکه آنها را به صورت دستی حذف میکردید.
ثبت و فعال سازی اولین Middleware
در ادامهی تکمیل و معرفی برنامهی خالی ASP.NET Core 1.0، قصد داریم یک Middleware توکار را به نام Welcome Page، بجای نمایش سطر Hello world پیش فرض این برنامه، ثبت و فعال سازی کنیم. این Middleware ویژه، در اسمبلی به نام Microsoft.AspNetCore.Diagnostics قرار دارد. اگر فایل project.json پیش فرض این پروژه را باز کنید، این اسمبلی به صورت پیش فرض در آن ثبت و معرفی شدهاست:
بنابراین هم اکنون بستهی نیوگت آن نیز به پروژهی جاری اضافه شده و قابل استفاده است. در غیراینصورت همانطور که در قسمت قبل در مطلب «نقش فایل project.json» عنوان شد، میتوان بر روی گره references کلیک راست کرده و سپس از منوی ظاهر شده،گزینهی manage nuget packages را انتخاب کرد. سپس، ابتدا برگهی browse را انتخاب کنید و در اینجا نام Microsoft.AspNetCore.Diagnostics را جستجو کرده و در آخر آنرا نصب کنید.
پس از اطمینان حاصل کردن از نصب بستهی نیوگت Microsoft.AspNetCore.Diagnostics، اکنون جهت معرفی Middleware توکار Welcome Page ، فایل Startup.cs را گشوده و کدهای آنرا به نحو ذیل تغییر دهید:
در اینجا نحوهی ثبت این middleware را با افزودن آن به IApplicationBuilder تزریق شدهی توسط OWIN، به کمک متد الحاقی UseWelcomePage مشاهده میکنید.
به علاوه middleware دومی را نیز با متد الحاقی Run مشاهده میکنید. به این نوع middlewareهای خاص، اصطلاحا terminal middleware میگویند. از این جهت که درخواستی را دریافت و یک response را تولید میکنند و کار همینجا خاتمه مییابد و زنجیرهی پردازشی middlewareها ادامه نخواهد یافت. در اینجا پارامتر context آن از نوع HttpContext است و باید دقت داشت، زمانیکه کار نوشتن در Response، در اینجا انجام شد، اگر پس از متد Run یک Middleware دیگر را ثبت کنید، هیچگاه اجرا نخواهد شد.
در این حالت اگر برنامه را اجرا کنید، خروجی ذیل را مشاهده خواهید کرد:
welcome page نیز یک terminal middleware است. به همین جهت middleware بعدی ثبت شدهی در اینجا یا همان متد Run، دیگر اجرا نخواهد شد.
در قسمتهای بعدی، تعداد بیشتری از Middlewareهای توکار ASP.NET Core 1.0 را بررسی خواهیم کرد.
- «با HttpHandler بیشتر آشنا شوید»
یکی از بزرگترین تغییرات ASP.NET Core نسبت به نگارشهای قبلی آن، مدیریت HTTP pipeline آن است. به عنوان یک توسعه دهندهی ASP.NET به طور قطع با مفاهیمی مانند HttpHandler و HttpModules آشنایی دارید و ... هر دوی اینها با نگارش جدید ASP.NET حذف و با مفهوم جدیدی به نام Middleware جایگزین شدهاند.
- HttpHandlerها عموما مبتنی بر پسوندهای فایلها عمل میکنند. برای نمونه، رایجترین آنها ASP.NET page handler است که هرگاه درخواستی، ختم شدهی به پسوند aspx، به موتور ASP.NET Web forms وارد شد، به این page handler، جهت پردازش نهایی هدایت میشود. محل تنظیم آنها نیز در فایل web.config است.
- HttpModuleها مبتنی بر رخدادها عمل میکنند. HttpModuleها به عنوان جزئی از request pipeline عمل کرده و دسترسی کاملی دارند به رخدادهای طول عمر درخواست رسیده. آنها را میتوان توسط فایلهای global.asax و یا web.config تنظیم کرد.
- MiddleWareها را که جزئی از طراحی OWIN نیز هستند، میتوان به عنوان کامپوننتهای کوچکی از برنامه که قابلیت یکپارچه شدن با HTTP request pipeline را دارند، درنظر گرفت. عملکرد آنها ترکیبی است از هر دوی HttpHandler و HttpModuleها که در نگارشهای قبلی ASP.NET مورد استفاده بودند. از MiddleWareها میتوان برای پیاده سازی اعمال مختلفی جهت پردازش درخواستهای رسیده مانند اعتبارسنجی، کار با سشنها، ثبت وقایع سیستم، مسیریابی و غیره استفاده کرد. OWIN یا Open Web Interface for .NET به توسعه دهندهها امکان غیروابسته کردن برنامهی ASP.NET خود را از وب سرور مورد استفاده میدهد. به علاوه OWIN امکان پلاگین نویسی اجزای مختلف برنامه را بدون وابسته کردن آنها به یکدیگر فراهم میکند. برای مثال میتوان یک پلاگین logger را تهیه کرد تا اطلاعات مختلفی را از درخواستهای رسیده ثبت کند.
مواردی که با ارائهی ASP.NET Core 1.0 حذف شدهاند
- System.Web: یکی از اهداف اصلی OWIN، مستقل کردن برنامهی وب، از هاست آن است تا بتوان از وب سرورهای بیشتری استفاده کرد. System.Web انحصارا برای IIS طراحی شده بود و در این نگارش دیگر وجود خارجی ندارد.
- HttpModules: با Middlewareها جایگزین شدهاند.
- HttpHandlers: البته HttpHandlers از زمان ارائهی اولین نگارش ASP.NET MVC، در چندین سال قبل منسوخ شدند. زیرا پردازش صفحات وب در ASP.NET MVC برخلاف وب فرمها، از فایلهایی با پسوندهای خاص شروع نمیشوند و نقطهی آغازین آنها، اکشن متدهای کنترلرها است.
- فایل global.asax: با حذف شدن HttpModules، دیگر ضرورتی به وجود این فایل نیست.
شباهتهای بینMiddleware و HttpModuleها
همانند HttpModuleها، Middlewareها نیز به ازای هر درخواست رسیده اجرا میشوند و از هر دو میتوان جهت تولید response ایی خاص استفاده کرد.
تفاوتهای بینMiddleware و HttpModuleها
- عموما HttpModule از طریق web.config و یا فایل global.asax تنظیم میشوند. اما Middlewareها تنها از طریق کد و در فایل Startup.cs برنامههای ASP.NET Core 1.0 قابل معرفی هستند.
- به عنوان یک توسعه دهنده، کنترلی را بر روی ترتیب اجرای انواع و اقسام HttpModuleها نداریم. این مشکل در Middlewareها برطرف شده و اکنون ترتیب اجرای آنها، دقیقا مطابق ترتیب افزوده شدن و تعریف آنها در فایل Startup.cs است.
- ترتیب اجرای HttpModuleها هرچه که باشد، برای حالتهای request و response یکی است. اما ترتیب اجرای Middlewareهای مخصوص response، عکس ترتیب اجرای Middlewareهای مخصوص request هستند (تصویر ذیل).
- HttpModuleها را صرفا جهت اتصال کدهایی به رخدادهای طول عمر برنامه میتوان طراحی کرد؛ اما Middleware مستقل هستند از این رخدادها.
- HttpModuleها به System.Web وابستهاند؛ برخلاف Middlewareها که وابستگی به هاست خود ندارند.
Middlewareهای توکار ASP.NET Core 1.0
ASP.NET Core 1.0 به همراه تعدادی Middleware توکار است؛ مانند:
- Authentication: جهت پشتیبانی از اعتبارسنجی
- CORS: برای فعال سازی Cross-Origin Resource Sharing
- Routing: جهت تعریف و محدود سازی مسیریابیهای برنامه
- Session: برای پشتیبانی و مدیریت سشنهای کاربران
- Diagnostics: پشتیبانی از صفحات خطا و اطلاعات زمان اجرای برنامه
و مواردی دیگر که برای توزیع فایلهای استاتیک و یا مرور محتویات پوشهها و امثال آنها طراحی شدهاند که در طی این مطلب و همچنین مطالب آتی، آنها را بررسی خواهیم کرد.
یک مثال: اگر یک پروژهی خالی ASP.NET Core 1.0 را در ویژوال استودیو ایجاد کنید، این پروژه قادر نیست حتی فایلهای استاتیک مانند تصاویر، فایلهای پیش فرض مانند index.html و یا قابلیت مرور سادهی فایلهای موجود در یک پوشه را ارائه دهد (حتی اگر به آنها نیازی نداشته باشید).
طراحی این نگارش از ASP.NET، مبتنی است بر سبک وزن بودن و ماژولار بودن. هر قابلیتی را که نیاز دارید، باید middleware آنرا نیز خودتان به صورت صریح اضافه کنید و یا در غیر اینصورت فعال نیست. برخلاف نگارشهای قبلی ASP.NET که HTTP Moduleهای از سشن گرفته تا اعتبارسنجی و غیره، همگی به صورت پیش فرض در فایل Web.Config فعال بودند؛ مگر اینکه آنها را به صورت دستی حذف میکردید.
ثبت و فعال سازی اولین Middleware
در ادامهی تکمیل و معرفی برنامهی خالی ASP.NET Core 1.0، قصد داریم یک Middleware توکار را به نام Welcome Page، بجای نمایش سطر Hello world پیش فرض این برنامه، ثبت و فعال سازی کنیم. این Middleware ویژه، در اسمبلی به نام Microsoft.AspNetCore.Diagnostics قرار دارد. اگر فایل project.json پیش فرض این پروژه را باز کنید، این اسمبلی به صورت پیش فرض در آن ثبت و معرفی شدهاست:
{ "dependencies": { "Microsoft.NETCore.App": { "version": "1.0.0", "type": "platform" }, "Microsoft.AspNetCore.Diagnostics": "1.0.0", "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0", "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0" },
پس از اطمینان حاصل کردن از نصب بستهی نیوگت Microsoft.AspNetCore.Diagnostics، اکنون جهت معرفی Middleware توکار Welcome Page ، فایل Startup.cs را گشوده و کدهای آنرا به نحو ذیل تغییر دهید:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace Core1RtmEmptyTest { public class Startup { public void ConfigureServices(IServiceCollection services) { } public void Configure(IApplicationBuilder app) { app.UseWelcomePage(); app.Run(async (context) => { await context.Response.WriteAsync("Hello DNT!"); }); } } }
به علاوه middleware دومی را نیز با متد الحاقی Run مشاهده میکنید. به این نوع middlewareهای خاص، اصطلاحا terminal middleware میگویند. از این جهت که درخواستی را دریافت و یک response را تولید میکنند و کار همینجا خاتمه مییابد و زنجیرهی پردازشی middlewareها ادامه نخواهد یافت. در اینجا پارامتر context آن از نوع HttpContext است و باید دقت داشت، زمانیکه کار نوشتن در Response، در اینجا انجام شد، اگر پس از متد Run یک Middleware دیگر را ثبت کنید، هیچگاه اجرا نخواهد شد.
در این حالت اگر برنامه را اجرا کنید، خروجی ذیل را مشاهده خواهید کرد:
welcome page نیز یک terminal middleware است. به همین جهت middleware بعدی ثبت شدهی در اینجا یا همان متد Run، دیگر اجرا نخواهد شد.
در قسمتهای بعدی، تعداد بیشتری از Middlewareهای توکار ASP.NET Core 1.0 را بررسی خواهیم کرد.
امکان کنترل کامل و سفارشی سازی ظاهر نهایی Swagger-UI وجود دارد که جزئیات آنرا در این قسمت بررسی خواهیم کرد.
بهبود ظاهر کامنتها با بکارگیری Markdown
Markdown زبان سبکی است برای تعیین شیوهنامهی نمایش متون ساده. اگر پیشتر با سیستم ارسال نظرات Github و یا Stackoverflow کار کرده باشید، قطعا با آن آشنایی دارید. توضیحات کامل و جزئیات آنرا میتوانید در آدرس markdownguide.org مطالعه کنید. خوشبختانه امکان استفادهی از Markdown در OpenAPI spec نیز وجود دارد که سبب بهبود ظاهر مستندات نهایی حاصل از آن خواهد شد.
در قسمت سوم، سعی کردیم مثالی را توسط remarks، به قسمت Patch اضافه کنیم که ظاهر آن، آنچنان مطلوب به نظر نمیرسد و بهتر است آنرا به صورت یک قطعه کد نمایش داد:
برای بهبود این ظاهر میتوان از Markdown استفاده کرد. بنابراین ابتدا تمام backslashهای اضافه شده را که جهت نمایش خطوط جدید اضافه شده بودند، حذف میکنیم. در Markdown خطوط جدید با درج حداقل 2 فاصله (space) و یک سطر جدید مشخص میشوند. همچنین استفادهی از ** سبب ضخیم شدن نمایش عبارتها میشود. برای اینکه قطعه کد نوشته شده را در Markdown به صورت کدی با پس زمینهای مشخص نمایش دهیم، پیش از شروع هر سطر آن نیاز است یک tab و یا 4 فاصله (space) درج شوند:
پس از این تغییرات خواهیم داشت:
در نگارش فعلی، استفادهی از Markdown برای توضیحات remarks، پارامترها و response codes پشتیبانی میشود؛ اما نه برای قسمت summary که برای نگارش بعدی درنظر گرفته شدهاست. همچنین از قابلیتهای پیشترفتهی Markdown هم استفاده نکنید (در کل نیاز به مقداری سعی و خطا دارد تا مشخص شود چه قابلیتهایی را پشتیبانی میکند).
سفارشی سازی مقدماتی UI به کمک تنظیمات API آن
جائیکه تنظیمات میانافزار Swashbuckle.AspNetCore در کلاس Starup تعریف میشوند، میتوان تغییراتی را نیز در UI آن اعمال کرد:
با این خروجی که به علت تنظیم DocExpansion آن به None، اینبار لیست قابلیتها را به صورت باز شده نمایش نمیدهد:
همچنین چون DefaultModelRendering به Model تنظیم شدهاست، اینبار بجای مثال، مشخصات مدل را به صورت پیشفرض نمایش میدهد:
کار DisplayOperationId نمایش Id هر Operation است؛ مانند get_api_authors. در اینجا Operation همان مداخل API ما هستند و به عنوان هر قسمت، Tag گفته میشود؛ مانند Authors و یا Books:
با فعالسازی EnableDeepLinking، آدرسهایی مانند tagName/# و یا tagName/OperationId/# قابلیت مرور و بازشدن خودکار را پیدا میکنند (tagName همان نام کنترلر است و OperationId همان اکشن متدی که عمومی شدهاست). برای مثال اگر آدرس https://localhost:5001/index.html#/Books/get_api_authors__authorId__books را در یک برگهی جدید مرورگر باز کنیم، بلافاصله گروه Books، باز شده و سپس به اکشن متد یا مدخلی که Id آن ذکر شدهاست، هدایت میشویم:
اعمال تغییرات پیشرفته در UI
Swagger-UI در اصل از یک سری فایل html، css، js و فونت تشکیل شدهاست که آنها را میتوانید در آدرس src/Swashbuckle.AspNetCore.SwaggerUI مشاهده کنید. برای مثال فایل index.html آنرا در اینجا میتوانید مشاهده کنید. اصل آن در div ای با id مساوی swagger-ui رخ میدهد و در این قسمت است که رابط کاربری آن به صورت پویا تولید شده و رندر خواهد شد. بررسی فایلهای js و css آن در این مخزن کد مشکل است؛ چون نگارش فشرده شدهی آن هستند. به همین جهت میتوان به مخزن کد اصلی Swagger-UI که نگارش جایگذاری شدهی آن (embedded) توسط Swashbuckle.AspNetCore ارائه میشود، رجوع کرد. برای مثال در پوشهی src/styles آن، اصل فایلهای css آن برای سفارشی سازی وجود دارند.
پس از اعمال تغییرات خود، میتوانید css و یا js سفارشی خود را به نحو زیر به تنظیمات app.UseSwaggerUI سیستم معرفی کنید:
باید دقت داشت که این فایلها باید در پوشهی wwwroot قرار گرفته و قابل دسترسی باشند.
برای اعمال تغییرات اساسی و تزریق صفحهی index.html جدیدی، میتوان به صورت زیر عمل کرد:
نکتهی مهم: اینبار این فایل باید به صورت embedded ارائه شود. به همین جهت در مثال فوق، عبارت OpenAPISwaggerDoc.Web به فضای نام اصلی اسمبلی جاری اشاره میکند. سپس EmbeddedAssets، نام پوشهای است که فایل index.html سفارشی سازی شده، در آن قرار خواهد گرفت. اکنون برای اینکه این فایل را به صورت EmbeddedResource معرفی کنیم، نیاز است فایل csproj را به نحو زیر ویرایش کرد:
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: OpenAPISwaggerDoc-07.zip
بهبود ظاهر کامنتها با بکارگیری Markdown
Markdown زبان سبکی است برای تعیین شیوهنامهی نمایش متون ساده. اگر پیشتر با سیستم ارسال نظرات Github و یا Stackoverflow کار کرده باشید، قطعا با آن آشنایی دارید. توضیحات کامل و جزئیات آنرا میتوانید در آدرس markdownguide.org مطالعه کنید. خوشبختانه امکان استفادهی از Markdown در OpenAPI spec نیز وجود دارد که سبب بهبود ظاهر مستندات نهایی حاصل از آن خواهد شد.
در قسمت سوم، سعی کردیم مثالی را توسط remarks، به قسمت Patch اضافه کنیم که ظاهر آن، آنچنان مطلوب به نظر نمیرسد و بهتر است آنرا به صورت یک قطعه کد نمایش داد:
برای بهبود این ظاهر میتوان از Markdown استفاده کرد. بنابراین ابتدا تمام backslashهای اضافه شده را که جهت نمایش خطوط جدید اضافه شده بودند، حذف میکنیم. در Markdown خطوط جدید با درج حداقل 2 فاصله (space) و یک سطر جدید مشخص میشوند. همچنین استفادهی از ** سبب ضخیم شدن نمایش عبارتها میشود. برای اینکه قطعه کد نوشته شده را در Markdown به صورت کدی با پس زمینهای مشخص نمایش دهیم، پیش از شروع هر سطر آن نیاز است یک tab و یا 4 فاصله (space) درج شوند:
/// <remarks> /// Sample request (this request updates the author's **first name**) /// /// PATCH /authors/id /// [ /// { /// "op": "replace", /// "path": "/firstname", /// "value": "new first name" /// } /// ] /// </remarks>
که نسبت به حالت قبلی بسیار بهتر به نظر میرسد.
در نگارش فعلی، استفادهی از Markdown برای توضیحات remarks، پارامترها و response codes پشتیبانی میشود؛ اما نه برای قسمت summary که برای نگارش بعدی درنظر گرفته شدهاست. همچنین از قابلیتهای پیشترفتهی Markdown هم استفاده نکنید (در کل نیاز به مقداری سعی و خطا دارد تا مشخص شود چه قابلیتهایی را پشتیبانی میکند).
سفارشی سازی مقدماتی UI به کمک تنظیمات API آن
جائیکه تنظیمات میانافزار Swashbuckle.AspNetCore در کلاس Starup تعریف میشوند، میتوان تغییراتی را نیز در UI آن اعمال کرد:
namespace OpenAPISwaggerDoc.Web { public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ... app.UseSwaggerUI(setupAction => { setupAction.SwaggerEndpoint( url: "/swagger/LibraryOpenAPISpecification/swagger.json", name: "Library API"); setupAction.RoutePrefix = ""; setupAction.DefaultModelExpandDepth(2); setupAction.DefaultModelRendering(Swashbuckle.AspNetCore.SwaggerUI.ModelRendering.Model); setupAction.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None); setupAction.EnableDeepLinking(); setupAction.DisplayOperationId(); }); // ... } } }
همچنین چون DefaultModelRendering به Model تنظیم شدهاست، اینبار بجای مثال، مشخصات مدل را به صورت پیشفرض نمایش میدهد:
کار DisplayOperationId نمایش Id هر Operation است؛ مانند get_api_authors. در اینجا Operation همان مداخل API ما هستند و به عنوان هر قسمت، Tag گفته میشود؛ مانند Authors و یا Books:
با فعالسازی EnableDeepLinking، آدرسهایی مانند tagName/# و یا tagName/OperationId/# قابلیت مرور و بازشدن خودکار را پیدا میکنند (tagName همان نام کنترلر است و OperationId همان اکشن متدی که عمومی شدهاست). برای مثال اگر آدرس https://localhost:5001/index.html#/Books/get_api_authors__authorId__books را در یک برگهی جدید مرورگر باز کنیم، بلافاصله گروه Books، باز شده و سپس به اکشن متد یا مدخلی که Id آن ذکر شدهاست، هدایت میشویم:
اعمال تغییرات پیشرفته در UI
Swagger-UI در اصل از یک سری فایل html، css، js و فونت تشکیل شدهاست که آنها را میتوانید در آدرس src/Swashbuckle.AspNetCore.SwaggerUI مشاهده کنید. برای مثال فایل index.html آنرا در اینجا میتوانید مشاهده کنید. اصل آن در div ای با id مساوی swagger-ui رخ میدهد و در این قسمت است که رابط کاربری آن به صورت پویا تولید شده و رندر خواهد شد. بررسی فایلهای js و css آن در این مخزن کد مشکل است؛ چون نگارش فشرده شدهی آن هستند. به همین جهت میتوان به مخزن کد اصلی Swagger-UI که نگارش جایگذاری شدهی آن (embedded) توسط Swashbuckle.AspNetCore ارائه میشود، رجوع کرد. برای مثال در پوشهی src/styles آن، اصل فایلهای css آن برای سفارشی سازی وجود دارند.
پس از اعمال تغییرات خود، میتوانید css و یا js سفارشی خود را به نحو زیر به تنظیمات app.UseSwaggerUI سیستم معرفی کنید:
setupAction.InjectStylesheet("/Assets/custom-ui.css"); setupAction.InjectJavaScript("/Assets/custom-js.js");
برای اعمال تغییرات اساسی و تزریق صفحهی index.html جدیدی، میتوان به صورت زیر عمل کرد:
setupAction.IndexStream = () => GetType().Assembly.GetManifestResourceStream( "OpenAPISwaggerDoc.Web.EmbeddedAssets.index.html");
<Project Sdk="Microsoft.NET.Sdk.Web"> <ItemGroup> <None Remove="EmbeddedAssets\index.html" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="EmbeddedAssets\index.html" /> </ItemGroup> </Project>
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: OpenAPISwaggerDoc-07.zip
با مطالعهی سورسهای محصولات اخیرا سورس باز شدهی مایکروسافت، نکات جالبی را میتوان استخراج کرد. برای نمونه اگر سورس پروژهی Orleans را بررسی کنیم، در حین بررسی اطلاعات استثناءهای رخ دادهی در برنامه، متد TraceLogger.CreateMiniDump نیز بکار رفتهاست. در این مطلب قصد داریم، این متد و نحوهی استفادهی از حاصل آنرا بررسی کنیم.
تولید MiniDump در برنامههای دات نت
خلاصهی روش تولید MiniDump در پروژهی Orleans به صورت زیر است:
الف) حالتهای مختلف تولید فایل دامپ که مقادیر آن قابلیت Or شدن را دارا هستند:
ب) متد توکار ویندوز برای تولید فایل دامپ
ج) فراخوانی متد تولید دامپ در برنامه
در اینجا نحوهی استفاده از enum و متد MiniDumpWriteDump ویندوز را مشاهده میکنید:
متد MiniDumpWriteDump نیاز به اطلاعات پروسهی جاری، به همراه هندل فایلی که قرار است اطلاعات را در آن بنویسد، دارد. همچنین dump type آن نیز میتواند ترکیبی از مقادیر enum مرتبط باشد.
یک مثال:
در اینجا نحوهی فراخوانی متد CreateMiniDump را در حین کرش برنامه مشاهده میکنید. پارامترهای اضافی دیگر سبب خواهند شد تا اطلاعات بیشتری از حافظهی جاری سیستم، در دامپ نهایی قرار گیرند. اگر پس از اجرای برنامه، به پوشهی bin\debug مراجعه کنید، فایل dmp تولیدی را مشاهده خواهید کرد.
نحوهی بررسی فایلهای dump
الف) با استفاده از Visual studio 2013
از به روز رسانی سوم VS 2013 به بعد، فایلهای dump را میتوان داخل خود VS.NET نیز آنالیز کرد (^ و ^ و ^). برای نمونه تصویر ذیل، حاصل کشودن فایل کرش مثال فوق است:
در اینجا اگر بر روی لینک debug managed memory کلیک کنید، پس از چند لحظه، آنالیز کامل اشیاء موجود در حافظه را در حین تهیهی دامپ تولیدی، میتوان مشاهده کرد. این مورد برای آنالیز نشتیهای حافظهی یک برنامه بسیار مفید است.
ب) استفاده از برنامهی Debug Diagnostic Tool
برنامهی Debug Diagnostic Tool را از اینجا میتوانید دریافت کنید. این برنامه نیز قابلیت آنالیز فایلهای دامپ را داشته و اطلاعات بیشتری را پس از آنالیز ارائه میدهد.
برای نمونه پس از آنالیز فایل دامپ تهیه شده توسط این برنامه، خروجی ذیل حاصل میشود:
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید:
MiniDumpTest.zip
تولید MiniDump در برنامههای دات نت
خلاصهی روش تولید MiniDump در پروژهی Orleans به صورت زیر است:
الف) حالتهای مختلف تولید فایل دامپ که مقادیر آن قابلیت Or شدن را دارا هستند:
[Flags] public enum MiniDumpType { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, MiniDumpWithFullMemory = 0x00000002, MiniDumpWithHandleData = 0x00000004, MiniDumpFilterMemory = 0x00000008, MiniDumpScanMemory = 0x00000010, MiniDumpWithUnloadedModules = 0x00000020, MiniDumpWithIndirectlyReferencedMemory = 0x00000040, MiniDumpFilterModulePaths = 0x00000080, MiniDumpWithProcessThreadData = 0x00000100, MiniDumpWithPrivateReadWriteMemory = 0x00000200, MiniDumpWithoutOptionalData = 0x00000400, MiniDumpWithFullMemoryInfo = 0x00000800, MiniDumpWithThreadInfo = 0x00001000, MiniDumpWithCodeSegs = 0x00002000, MiniDumpWithoutManagedState = 0x00004000 }
ب) متد توکار ویندوز برای تولید فایل دامپ
public static class NativeMethods { [DllImport("Dbghelp.dll")] public static extern bool MiniDumpWriteDump( IntPtr hProcess, int processId, IntPtr hFile, MiniDumpType dumpType, IntPtr exceptionParam, IntPtr userStreamParam, IntPtr callbackParam); }
ج) فراخوانی متد تولید دامپ در برنامه
در اینجا نحوهی استفاده از enum و متد MiniDumpWriteDump ویندوز را مشاهده میکنید:
public static class DebugInfo { public static void CreateMiniDump( string dumpFileName, MiniDumpType dumpType = MiniDumpType.MiniDumpNormal) { using (var stream = File.Create(dumpFileName)) { var process = Process.GetCurrentProcess(); // It is safe to call DangerousGetHandle() here because the process is already crashing. NativeMethods.MiniDumpWriteDump( process.Handle, process.Id, stream.SafeFileHandle.DangerousGetHandle(), dumpType, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); } } public static void CreateMiniDump(MiniDumpType dumpType = MiniDumpType.MiniDumpNormal) { const string dateFormat = "yyyy-MM-dd-HH-mm-ss-fffZ"; // Example: 2010-09-02-09-50-43-341Z var thisAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly(); var dumpFileName = string.Format(@"{0}-MiniDump-{1}.dmp", thisAssembly.GetName().Name, DateTime.UtcNow.ToString(dateFormat, CultureInfo.InvariantCulture)); var path = Path.Combine(getApplicationPath(), dumpFileName); CreateMiniDump(path, dumpType); } private static string getApplicationPath() { return HttpContext.Current != null ? HttpRuntime.AppDomainAppPath : Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } }
یک مثال:
class Program { static void Main(string[] args) { try { var zero = 0; Console.WriteLine(1 / zero); } catch (Exception ex) { Console.Write(ex); DebugInfo.CreateMiniDump(dumpType: MiniDumpType.MiniDumpNormal | MiniDumpType.MiniDumpWithPrivateReadWriteMemory | MiniDumpType.MiniDumpWithDataSegs | MiniDumpType.MiniDumpWithHandleData | MiniDumpType.MiniDumpWithFullMemoryInfo | MiniDumpType.MiniDumpWithThreadInfo | MiniDumpType.MiniDumpWithUnloadedModules); throw; } } }
نحوهی بررسی فایلهای dump
الف) با استفاده از Visual studio 2013
از به روز رسانی سوم VS 2013 به بعد، فایلهای dump را میتوان داخل خود VS.NET نیز آنالیز کرد (^ و ^ و ^). برای نمونه تصویر ذیل، حاصل کشودن فایل کرش مثال فوق است:
در اینجا اگر بر روی لینک debug managed memory کلیک کنید، پس از چند لحظه، آنالیز کامل اشیاء موجود در حافظه را در حین تهیهی دامپ تولیدی، میتوان مشاهده کرد. این مورد برای آنالیز نشتیهای حافظهی یک برنامه بسیار مفید است.
ب) استفاده از برنامهی Debug Diagnostic Tool
برنامهی Debug Diagnostic Tool را از اینجا میتوانید دریافت کنید. این برنامه نیز قابلیت آنالیز فایلهای دامپ را داشته و اطلاعات بیشتری را پس از آنالیز ارائه میدهد.
برای نمونه پس از آنالیز فایل دامپ تهیه شده توسط این برنامه، خروجی ذیل حاصل میشود:
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید:
MiniDumpTest.zip
به C# 11، عملگر جدیدی به شکل <<< و به معنای unsigned right shift اضافه شدهاست که ... در زبان جاوا از نگارش ابتدایی آن حضور داشتهاست. اما ... چرا از این لحاظ بین این دو زبان، تفاوت وجود داشتهاست؟
مفهوم عملگر شیفت در #C
عملگر شیفت به سمت راست و یا <<، عددی را به تعداد بیت مشخص شده (x >> count)، به سمت راست منتقل میکند و دو نوع دارد:
الف) شیفت به راست منطقی
برای مثال اگر عدد 12 را به صورت باینری نمایش دهیم، به صورت زیر خواهد بود:
و اگر آنرا به اندازهی یک بیت به سمت راست هدایت کنیم، که با 1 <<< 12 نمایش داده میشود:
به عدد 6 خواهیم رسید.
در این حالت همواره فرض میشود که عدد مدنظر، unsigned است.
ب) شیفت به راست ریاضی
شیفت به راست ریاضی، دقیقا مانند شیفت به راست منطقی است؛ مانند مثال زیر که عدد 1001 باینری را دو بیت به سمت راست منتقل میکند:
اما ... بجای اینکه همانند شیفت به راست منطقی، سمت چپ را با صفر پر کند، آنرا با «با ارزشترین بیت یا همان بیت علامت» پر میکند. یعنی در اینجا بیتی که بیانگر مثبت و منفی بودن عدد است، حفظ میشود. یعنی این نوع شیفت، با اعداد signed هم کار میکند.
برای مثال نمایش باینری عدد منفی 2,147,483,552- به صورت زیر است:
و اگر آنرا چهار بیت به سمت راست هدایت کنیم (یعنی 4 << 2,147,483,552 -)، به عدد 134,217,722- میرسیم که معادل عدد باینری زیر است:
به این ترتیب با شیفت به راست ریاضی، علامت عدد منفی حفظ شدهاست. مثالی دیگر:
سؤال: در زبان جاوا، عملگر <<< به معنای unsigned right shift است؛ اما چنین عملگری در زبان #C تا نگارش 11 آن وجود ندارد؛ چرا؟!
تا پیش از C# 11 اگر نیاز به کار بر روی signed types جهت رسیدن به نتیجهی عملگر <<< وجود داشته باشد (انجام شیفت منطقی؛ یعنی صرفنظر کردن از نوع علامت عدد)، میتوان از متدهای الحاقی زیر استفاده کرد که ابتدا آنها را به نمونههای unsigned تبدیل میکند و کار شیفت را انجام میدهد و سپس نوع اصلی را بازیابی میکند:
مفهوم عملگر شیفت در #C
عملگر شیفت به سمت راست و یا <<، عددی را به تعداد بیت مشخص شده (x >> count)، به سمت راست منتقل میکند و دو نوع دارد:
الف) شیفت به راست منطقی
برای مثال اگر عدد 12 را به صورت باینری نمایش دهیم، به صورت زیر خواهد بود:
00000000 00000000 00000000 00001100
00000000 00000000 00000000 00000110
در این حالت همواره فرض میشود که عدد مدنظر، unsigned است.
ب) شیفت به راست ریاضی
شیفت به راست ریاضی، دقیقا مانند شیفت به راست منطقی است؛ مانند مثال زیر که عدد 1001 باینری را دو بیت به سمت راست منتقل میکند:
uint e = 0b_1001; Console.WriteLine($"Before: {Convert.ToString(e, toBase: 2),4}"); // Before: 1001 uint f = e >> 2; Console.WriteLine($"After: {Convert.ToString(f, toBase: 2).PadLeft(4, '0'),4}"); // After: 0010
برای مثال نمایش باینری عدد منفی 2,147,483,552- به صورت زیر است:
10000000 00000000 00000000 01100000
11111000 00000000 00000000 00000110
int x = -8; Console.WriteLine($"Before: {x,11}, hex: {x,8:x}, binary: {Convert.ToString(x, toBase: 2),32}"); // Before: -8, hex: fffffff8, binary: 11111111111111111111111111111000 int y = x >> 2; Console.WriteLine($"After >>: {y,11}, hex: {y,8:x}, binary: {Convert.ToString(y, toBase: 2),32}"); // After >>: -2, hex: fffffffe, binary: 11111111111111111111111111111110 int z = x >>> 2; Console.WriteLine($"After >>>: {z,11}, hex: {z,8:x}, binary: {Convert.ToString(z, toBase: 2).PadLeft(32, '0'),32}"); // After >>>: 1073741822, hex: 3ffffffe, binary: 00111111111111111111111111111110
سؤال: در زبان جاوا، عملگر <<< به معنای unsigned right shift است؛ اما چنین عملگری در زبان #C تا نگارش 11 آن وجود ندارد؛ چرا؟!
پاسخ: چون زبان جاوا، فاقد نوعهای دادهای توکار unsigned integers است (^)؛ برخلاف #C از نگارش یک آن. حتی به ظاهر، این امکان در Java 8 اضافه شدهاست، اما در حقیقت با نوعهای int و long، فقط مانند اینکه unsigned هم میتوانند باشند، رفتار میکند (^). البته نوع char در زبان Java، تنها نوع unsigned واقعی است.
همانطور که عنوان شد، در زبان #C فقط کافی است بر روی unsigned types مانند ulong, uint, ushort، عملگر << را بکار برد تا به unsigned right shift جاوا رسید (به همین جهت عملگر اضافهتری برای آن ارائه نشده بود). البته باید دقت داشت که در اینجا عملگر << کار پر کردن MSB یا «با ارزشترین بیت یا همان بیت علامت» را هم با صفر انجام میدهد؛ حتی اگر MSB مقدار دهی شده باشد (چون این کاری است که << بر روی unsigned types انجام میدهد).تا پیش از C# 11 اگر نیاز به کار بر روی signed types جهت رسیدن به نتیجهی عملگر <<< وجود داشته باشد (انجام شیفت منطقی؛ یعنی صرفنظر کردن از نوع علامت عدد)، میتوان از متدهای الحاقی زیر استفاده کرد که ابتدا آنها را به نمونههای unsigned تبدیل میکند و کار شیفت را انجام میدهد و سپس نوع اصلی را بازیابی میکند:
public static int UnsignedRightShift(this int signed, int places) { unchecked { var unsigned = (uint)signed; unsigned >>= places; return (int)unsigned; } } public static long UnsignedRightShift(this long signed, int places) { unchecked { var unsigned = (ulong)signed; unsigned >>= places; return (long)unsigned; } }
توابع محلی، امکان تعریف یک تابع را درون یک متد، فراهم میکنند. هدف آنها تدارک توابعی کمکی است که به سایر قسمتهای کلاس مرتبط نمیشوند. برای مثال اگر متدی نیاز به کار با یک private method دیگر را دارد و این متد خصوصی در جای دیگری استفاده نمیشود، میتوان جهت بالابردن خوانایی برنامه و سهولت یافتن متد مرتبط، این متد خصوصی را تبدیل به یک تابع محلی، درون همان متد کرد.
بازنویسی کدهای C# 6 با توابع محلی C# 7
کلاس زیر را که بر اساس امکانات C# 6 تهیه شدهاست، در نظر بگیرید:
متد خصوصی همین کلاس را توسط Func delegates میتوان به صورت ذیل خلاصه کرد (باز هم بر اساس امکانات C# 6):
به این ترتیب نیاز به تعریف یک متد private دیگر کمتر خواهد شد.
اکنون در C# 7 میتوان این Func delegate را به نحو ذیل تبدیل به یک local function کرد:
مزیت کار با local functions نسبت به Func delegates محلی
در قطعه کد فوق، کار انجام شده صرفا استفادهی از یک Syntax جدید نیست؛ بلکه از لحاظ کارآیی نیز سربار کمتری را به همراه دارد. زمانیکه Func Delegates تعریف میشوند، کار ایجاد یک anonymous type، وهله سازی و فراخوانی آنها توسط کامپایلر صورت میگیرد. اما حین کار با توابع محلی، کامپایلر با یک متد استاندارد سروکار دارد و هیچکدام از مراحل یاد شده و سربارهای آنها رخ نمیدهند (هیچگونه GC allocation ایی نخواهیم داشت). به علاوه اینبار کامپایلر فرصت in-line تعریف کردن متد را به نحو بهتری یافته و به این ترتیب کار سوئیچ بین متدهای مختلف کاهش پیدا میکند که در نهایت سرعت برنامه را افزایش میدهند.
میدان دید توابع محلی
البته با توجه به اینکه متد مثال فوق محلی است، به تمام متغیرها و پارامترهای متد دربرگیرندهی آن نیز دسترسی دارد. بنابراین میتوان پارامتر int age آنرا نیز حذف کرد:
به همین جهت نمیتوانید داخل یک تابع محلی، متغیری را تعریف کنید که همنام یکی از متغیرها یا پارامترهای متد دربرگیرندهی آن باشد.
خلاصه نویسی توابع محلی به کمک expression bodies
میتوان این متد محلی را به صورت یک expression body ارائه شدهی در C# 6 نیز بیان کرد:
روش ارسال یک local function به متدی دیگر
امکان ارسال یک تابع محلی به صورت یک Func delegate به متدی دیگر نیز وجود دارد:
در این مثال GenerateAgeSuffix یک Local function است که به صورت expression body نیز بیان شدهاست. برای ارسال آن به متد OutputSimplePerson، پارامتر دریافتی آن باید به صورت Func تعریف شود.
static void Main(string[] args) { int Add(int a, int b) { return a + b; } Console.WriteLine(Add(3, 4)); }
بازنویسی کدهای C# 6 با توابع محلی C# 7
کلاس زیر را که بر اساس امکانات C# 6 تهیه شدهاست، در نظر بگیرید:
public class PersonWithPrivateMethod { public string Name { get; set; } public int Age { get; set; } public override string ToString() { string ageSuffix = GenerateAgeSuffix(Age); return $"{Name} is {Age} year{ageSuffix} old"; } private string GenerateAgeSuffix(int age) { return age > 1 ? "s" : ""; } }
public class PersonWithLocalFuncDelegate { public string Name { get; set; } public int Age { get; set; } public override string ToString() { Func<int, string> generateAgeSuffix = age => age > 1 ? "s" : ""; return $"{Name} is {Age} year{generateAgeSuffix(Age)} old"; } }
اکنون در C# 7 میتوان این Func delegate را به نحو ذیل تبدیل به یک local function کرد:
public class PersonWithLocalFunction { public string Name { get; set; } public int Age { get; set; } public override string ToString() { return $"{Name} is {Age} year{GenerateAgeSuffix(Age)} old"; // Define a local function: string GenerateAgeSuffix(int age) { return age > 1 ? "s" : ""; } } }
مزیت کار با local functions نسبت به Func delegates محلی
در قطعه کد فوق، کار انجام شده صرفا استفادهی از یک Syntax جدید نیست؛ بلکه از لحاظ کارآیی نیز سربار کمتری را به همراه دارد. زمانیکه Func Delegates تعریف میشوند، کار ایجاد یک anonymous type، وهله سازی و فراخوانی آنها توسط کامپایلر صورت میگیرد. اما حین کار با توابع محلی، کامپایلر با یک متد استاندارد سروکار دارد و هیچکدام از مراحل یاد شده و سربارهای آنها رخ نمیدهند (هیچگونه GC allocation ایی نخواهیم داشت). به علاوه اینبار کامپایلر فرصت in-line تعریف کردن متد را به نحو بهتری یافته و به این ترتیب کار سوئیچ بین متدهای مختلف کاهش پیدا میکند که در نهایت سرعت برنامه را افزایش میدهند.
میدان دید توابع محلی
البته با توجه به اینکه متد مثال فوق محلی است، به تمام متغیرها و پارامترهای متد دربرگیرندهی آن نیز دسترسی دارد. بنابراین میتوان پارامتر int age آنرا نیز حذف کرد:
public class PersonWithLocalFunctionEnclosing { public string Name { get; set; } public int Age { get; set; } public override string ToString() { return $"{Name} is {Age} year{GenerateAgeSuffix()} old"; // Define a local function: string GenerateAgeSuffix() { return Age > 1 ? "s" : ""; } } }
خلاصه نویسی توابع محلی به کمک expression bodies
میتوان این متد محلی را به صورت یک expression body ارائه شدهی در C# 6 نیز بیان کرد:
public class PersonWithLocalFunctionExpressionBodied { public string Name { get; set; } public int Age { get; set; } public override string ToString() { return $"{Name} is {Age} year{GenerateAgeSuffix(Age)} old"; // Define a local function: string GenerateAgeSuffix(int age) => age > 1 ? "s" : ""; } }
روش ارسال یک local function به متدی دیگر
امکان ارسال یک تابع محلی به صورت یک Func delegate به متدی دیگر نیز وجود دارد:
public class LocalFunctionsTest { public void PassAnonFunctionToMethod() { var p = new SimplePerson { Name = "Name1", Age = 42 }; OutputSimplePerson(p, GenerateAgeSuffix); string GenerateAgeSuffix(int age) => age > 1 ? "s" : ""; } private void OutputSimplePerson(SimplePerson person, Func<int, string> suffixFunction) { Output.WriteLine( $"{person.Name} is {person.Age} year{suffixFunction(person.Age)} old"); } }
پس از معرفی ابتدایی VSCode و نصب افزونهی #C در قسمت قبل، در ادامه میخواهیم اولین پروژهی ASP.NET Core خود را در آن ایجاد کنیم.
نصب ASP.NET Core بر روی سیستم عاملهای مختلف
برای نصب پیشنیازهای کار با ASP.NET Core به آدرس https://www.microsoft.com/net/download/core مراجعه کرده و NET Core SDK. را دریافت و نصب کنید. پس از نصب آن جهت اطمینان از صحت انجام عملیات، دستور dotnet --version را در خط فرمان صادر کنید:
در اینجا SDK نصب شده، شامل هر دو نگارش 1.0 و 1,1 است. همچنین در همین صفحهی دریافت فایلها، علاوه بر نگارش ویندوز، نگارشهای Mac و لینوکس آن نیز موجود هستند. بر روی هر کدام که کلیک کنید، ریز مراحل نصب هم به همراه آنها وجود دارد. برای مثال نصب NET Core. بر روی Mac شامل نصب OpenSSL به صورت جداگانه است و یا نصب آن بر روی لینوکس به همراه چند دستور مختص به توزیع مورد استفاده میباشد که به خوبی مستند شدهاند و نیازی به تکرار آنها نیست و همواره آخرین نگارش آنها بر روی سایت dot.net موجود است.
ایجاد اولین پروژهی ASP.NET Core توسط NET Core SDK.
پس از نصب NET Core SDK.، به پیشنیاز دیگری برای شروع به کار با ASP.NET Core نیازی نیست. نه نیازی است تا آخرین نگارش ویژوال استودیوی کامل را نصب کنید و نه با به روز رسانی آن، نیاز است چندگیگابایت فایل به روز رسانی تکمیلی را دریافت کرد. همینقدر که این SDK نصب شود، میتوان بلافاصله شروع به کار با این نگارش جدید کرد.
در ادامه ابتدا پوشهی جدید پروژهی خود را ساخته (برای مثال در مسیر D:\vs-code-examples\FirstAspNetCoreProject) و سپس از طریق خط فرمان به این پوشه وارد میشویم.
یک نکته: در ویندوزهای جدید فقط کافی است در نوار آدرس بالای صفحه تایپ کنید cmd. به این صورت بلافاصله command prompt استاندارد ویندوز در پوشهی جاری در دسترس خواهد بود و دیگر نیازی نیست تا چند مرحله را جهت رسیدن به آن طی کرد.
پس از وارد شدن به پوشهی جدید از طریق خط فرمان، دستور dotnet new --help را صادر کنید:
همانطور که مشاهده میکنید، اینبار بجای انتخاب گزینههای مختلف از صفحه دیالوگ new project template داخل ویژوال استودیوی کامل، تمام این قالبها از طریق خط فرمان در اختیار ما هستند. برای مثال میتوان یک برنامه کنسول و یا یک کتابخانهی جدید را ایجاد کرد.
در ادامه دستور ذیل را صادر کنید:
به این ترتیب یک پروژهی جدید ASP.NET Core، بدون تنظیمات اعتبارسنجی و ASP.NET Core Identity، در کسری از ثانیه ایجاد خواهد شد.
سپس جهت گشودن این پروژه در VSCode تنها کافی است دستور ذیل را صادر کنیم:
در ادامه یکی از فایلهای #C آنرا گشوده و منتظر شوید تا کار دریافت خودکار بستههای مرتبط با افزونهی #C ایی که در قسمت قبل نصب کردیم به پایان برسند:
اینکار یکبار باید انجام شود و پس از آن امکانات زبان #C و همچنین دیباگر NET Core. در VS Code قابل استفاده خواهند بود.
در تصویر فوق دو اخطار را هم مشاهده میکنید. مورد اول برای فعال سازی دیباگ و ساخت پروژهی جاری است. گزینهی Yes آنرا انتخاب کنید و اخطار دوم جهت بازیابی بستههای نیوگت پروژهاست که گزینهی restore آنرا انتخاب نمائید. البته کار بازیابی بستهها از طریق کش موجود در سیستم به سرعت انجام خواهد شد.
گشودن کنسول از درون VS Code و اجرای برنامهی ASP.NET Core
روشهای متعددی برای گشودن کنسول خط فرمان در VS Code وجود دارند:
الف) فشردن دکمههای Ctrl+Shift+C
اینکار باعث میشود تا command prompt ویندوز از پوشهی جاری به صورت مجزایی اجرا شود.
ب) فشردن دکمههای Ctrl+` (و یا Ctrl+ back-tick)
به این ترتیب کنسول پاورشل درون خود VS Code باز خواهد شد. اگر علاقمند نیستید تا از پاورشل استفاده کنید، میتوانید این پیشفرض را به نحو ذیل بازنویسی کنید:
همانطور که در قسمت قبل نیز ذکر شد، از طریق منوی File->Preferences->Settings میتوان به تنظیمات VS Code دسترسی یافت. پس از گشودن آن، یک سطر ذیل را به آن اضافه کنید:
اکنون Ctrl+ back-tick را فشرده تا کنسول خط فرمان، داخل VS Code نمایان شود و سپس دستور ذیل را صادر کنید:
در اینجا دستور dotnet run سبب کامپایل و همچنین اجرای پروژه شدهاست و اکنون این برنامهی وب بر روی پورت 5000 قابل دسترسی است:
ساده سازی ساخت و اجرای یک برنامهی ASP.NET Core در VS Code
زمانیکه گزینهی افزودن امکانات ساخت و دیباگ را انتخاب میکنیم (تصویر فوق)، دو فایل جدید به پوشهی vscode. اضافه میشوند:
دراینجا فایل tasks.json، حاوی وظیفهای است جهت ساخت برنامه. یعنی بجای اینکه مدام بخواهیم به خط فرمان مراجعه کرده و دستوراتی را صادر کنیم، میتوان از وظایفی که در پشت صحنه همین فرامین را اجرا میکنند، استفاده کنیم؛ که نمونهای از آن، به صورت پیش فرض به پروژه اضافه شده است.
برای دسترسی به آن، دکمههای ctrl+shift+p را فشرده و سپس در منوی ظاهر شده، واژهی build را جستجو کنید:
با انتخاب این گزینه (که توسط Ctrl+Shift+B هم در دسترس است)، کار ساخت برنامه انجام شده و dll مرتبط با آن در پوشهی bin تشکیل میشود.
همچنین در اینجا برای ساخت و بلافاصله نمایش آن در مرورگر پیش فرض سیستم، میتوان مجددا دکمههای ctrl+shift+p را فشرد و در منوی ظاهر شده، واژهی without را جستجو کرد:
با انتخاب این گزینه (که توسط Ctrl+F5 نیز در دسترس است)، برنامه ساخته شده، اجرا و نمایش داده میشود و برای خاتمهی آن میتوانید دکمههای Ctrl+C را بفشارید تا کار وب سرور موقتی آن خاتمه یابد.
در قسمت بعد مباحث دیباگ برنامه و گردش کار متداول یک پروژهی ASP.NET Core را بررسی خواهیم کرد.
نصب ASP.NET Core بر روی سیستم عاملهای مختلف
برای نصب پیشنیازهای کار با ASP.NET Core به آدرس https://www.microsoft.com/net/download/core مراجعه کرده و NET Core SDK. را دریافت و نصب کنید. پس از نصب آن جهت اطمینان از صحت انجام عملیات، دستور dotnet --version را در خط فرمان صادر کنید:
C:\>dotnet --version 1.0.1
ایجاد اولین پروژهی ASP.NET Core توسط NET Core SDK.
پس از نصب NET Core SDK.، به پیشنیاز دیگری برای شروع به کار با ASP.NET Core نیازی نیست. نه نیازی است تا آخرین نگارش ویژوال استودیوی کامل را نصب کنید و نه با به روز رسانی آن، نیاز است چندگیگابایت فایل به روز رسانی تکمیلی را دریافت کرد. همینقدر که این SDK نصب شود، میتوان بلافاصله شروع به کار با این نگارش جدید کرد.
در ادامه ابتدا پوشهی جدید پروژهی خود را ساخته (برای مثال در مسیر D:\vs-code-examples\FirstAspNetCoreProject) و سپس از طریق خط فرمان به این پوشه وارد میشویم.
یک نکته: در ویندوزهای جدید فقط کافی است در نوار آدرس بالای صفحه تایپ کنید cmd. به این صورت بلافاصله command prompt استاندارد ویندوز در پوشهی جاری در دسترس خواهد بود و دیگر نیازی نیست تا چند مرحله را جهت رسیدن به آن طی کرد.
پس از وارد شدن به پوشهی جدید از طریق خط فرمان، دستور dotnet new --help را صادر کنید:
D:\vs-code-examples\FirstAspNetCoreProject>dotnet new --help Getting ready... Template Instantiation Commands for .NET Core CLI. Usage: dotnet new [arguments] [options] Arguments: template The template to instantiate. Options: -l|--list List templates containing the specified name. -lang|--language Specifies the language of the template to create -n|--name The name for the output being created. If no name is specified, the name of the current directory is used. -o|--output Location to place the generated output. -h|--help Displays help for this command. -all|--show-all Shows all templates Templates Short Name Language Tags ---------------------------------------------------------------------- Console Application console [C#], F# Common/Console Class library classlib [C#], F# Common/Library Unit Test Project mstest [C#], F# Test/MSTest xUnit Test Project xunit [C#], F# Test/xUnit ASP.NET Core Empty web [C#] Web/Empty ASP.NET Core Web App mvc [C#], F# Web/MVC ASP.NET Core Web API webapi [C#] Web/WebAPI Solution File sln Solution Examples: dotnet new mvc --auth None --framework netcoreapp1.1 dotnet new xunit --framework netcoreapp1.1 dotnet new --help
در ادامه دستور ذیل را صادر کنید:
D:\vs-code-examples\FirstAspNetCoreProject>dotnet new mvc --auth None
سپس جهت گشودن این پروژه در VSCode تنها کافی است دستور ذیل را صادر کنیم:
D:\vs-code-examples\FirstAspNetCoreProject>code .
اینکار یکبار باید انجام شود و پس از آن امکانات زبان #C و همچنین دیباگر NET Core. در VS Code قابل استفاده خواهند بود.
در تصویر فوق دو اخطار را هم مشاهده میکنید. مورد اول برای فعال سازی دیباگ و ساخت پروژهی جاری است. گزینهی Yes آنرا انتخاب کنید و اخطار دوم جهت بازیابی بستههای نیوگت پروژهاست که گزینهی restore آنرا انتخاب نمائید. البته کار بازیابی بستهها از طریق کش موجود در سیستم به سرعت انجام خواهد شد.
گشودن کنسول از درون VS Code و اجرای برنامهی ASP.NET Core
روشهای متعددی برای گشودن کنسول خط فرمان در VS Code وجود دارند:
الف) فشردن دکمههای Ctrl+Shift+C
اینکار باعث میشود تا command prompt ویندوز از پوشهی جاری به صورت مجزایی اجرا شود.
ب) فشردن دکمههای Ctrl+` (و یا Ctrl+ back-tick)
به این ترتیب کنسول پاورشل درون خود VS Code باز خواهد شد. اگر علاقمند نیستید تا از پاورشل استفاده کنید، میتوانید این پیشفرض را به نحو ذیل بازنویسی کنید:
همانطور که در قسمت قبل نیز ذکر شد، از طریق منوی File->Preferences->Settings میتوان به تنظیمات VS Code دسترسی یافت. پس از گشودن آن، یک سطر ذیل را به آن اضافه کنید:
"terminal.integrated.shell.windows": "cmd.exe"
D:\vs-code-examples\FirstAspNetCoreProject>dotnet run Hosting environment: Production Content root path: D:\vs-code-examples\FirstAspNetCoreProject Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down.
ساده سازی ساخت و اجرای یک برنامهی ASP.NET Core در VS Code
زمانیکه گزینهی افزودن امکانات ساخت و دیباگ را انتخاب میکنیم (تصویر فوق)، دو فایل جدید به پوشهی vscode. اضافه میشوند:
دراینجا فایل tasks.json، حاوی وظیفهای است جهت ساخت برنامه. یعنی بجای اینکه مدام بخواهیم به خط فرمان مراجعه کرده و دستوراتی را صادر کنیم، میتوان از وظایفی که در پشت صحنه همین فرامین را اجرا میکنند، استفاده کنیم؛ که نمونهای از آن، به صورت پیش فرض به پروژه اضافه شده است.
برای دسترسی به آن، دکمههای ctrl+shift+p را فشرده و سپس در منوی ظاهر شده، واژهی build را جستجو کنید:
با انتخاب این گزینه (که توسط Ctrl+Shift+B هم در دسترس است)، کار ساخت برنامه انجام شده و dll مرتبط با آن در پوشهی bin تشکیل میشود.
همچنین در اینجا برای ساخت و بلافاصله نمایش آن در مرورگر پیش فرض سیستم، میتوان مجددا دکمههای ctrl+shift+p را فشرد و در منوی ظاهر شده، واژهی without را جستجو کرد:
با انتخاب این گزینه (که توسط Ctrl+F5 نیز در دسترس است)، برنامه ساخته شده، اجرا و نمایش داده میشود و برای خاتمهی آن میتوانید دکمههای Ctrl+C را بفشارید تا کار وب سرور موقتی آن خاتمه یابد.
در قسمت بعد مباحث دیباگ برنامه و گردش کار متداول یک پروژهی ASP.NET Core را بررسی خواهیم کرد.
پیشنهادها
C# 6 - String Interpolation
- C#/.NET Little Wonders: String Interpolation in C# 6
- Customizing string interpolation in C# 6
- C#6: String Interpolation
- String Interpolation C# 6
- String interpolation or String handling in C# 6.0 with New Feature
- C# 6 string interpolation is not a templating engine, and it’s not the new String.Format
- String Interpolation and the Conditional Operators