مطالب
VS Code برای توسعه دهندگان ASP.NET Core - قسمت اول - نصب و راه اندازی
VS Code یک محیط توسعه‌ی یکپارچه است که توسط مایکروسافت توسعه پیدا می‌کند و دارای مزایای ذیل است:
 - سبک وزن است
 - بسیار سریع است
 - به صورت سورس باز توسعه پیدا می‌کند
 - رایگان است
 - چندسکویی است
 - انواع و اقسام زبان‌های برنامه نویسی را پشتیبانی می‌کند
 - پشتیبانی بسیار مناسبی را از طرف جامعه‌ی برنامه نویسان به همراه دارد
 - به همراه تعداد زیادی افزونه است

هدف اصلی از توسعه‌ی آن نیز ارائه‌ی تجربه‌ی کاربری یکسانی در سکوهای کاری مختلف و زبان‌های متفاوت برنامه نویسی است. در اینجا مهم نیست که از ویندوز، مک یا لینوکس استفاده می‌کنید. نحوه‌ی کار کردن با آن در این سکوهای کاری تفاوتی نداشته و یکسان است. همچنین برای آن تفاوتی نمی‌کند که با PHP کار می‌کنید یا ASP.NET. تمام گروه‌های مختلف برنامه نویسان دسترسی به یک IDE بسیار سریع و سبک وزن را خواهند داشت.
برخلاف نگارش کامل ویژوال استودیو که این روزها حجم دریافت آن به بالای 20 گیگابایت رسیده‌است، VS Code با هدف سبک وزن بودن و سادگی دریافت و نصب، طراحی و توسعه پیدا می‌کند. این مورد، مزیت دریافت به روز رسانی‌های منظم را بدون نگرانی از دریافت حجم‌های بالا، برای بسیاری از علاقمندان مسیر می‌کند.
همچنین برای کار با نگارش‌های جدیدتر ASP.NET Core، دیگر نیازی به دریافت آخرین به روز رسانی‌های چندگیگابایتی ویژوال استودیوی کامل نبوده و می‌توان کاملا مستقل از آن، از آخرین نگارش NET Core. و ASP.NET Core به سادگی در VSCode استفاده کرد.


نصب VS Code بر روی ویندوز

آخرین نگارش این محصول را از آدرس https://code.visualstudio.com می‌توانید دریافت کنید. نصب آن نیز بسیار ساده‌است؛ فقط گزینه‌ی Add to PATH را نیز در حین نصب حتما انتخاب نمائید (هرچند به صورت پیش فرض نیز انتخاب شده‌است). به این ترتیب امکان استفاده‌ی از آن در کنسول‌های متفاوتی مسیر خواهد شد.
در ادامه فرض کنید که مسیر D:\vs-code-examples\sample01 حاوی اولین برنامه‌ی ما خواهد بود. برای اینکه در اینجا بتوانیم، تجربه‌ی کاربری یکسانی را مشاهده کنیم، از طریق خط فرمان به این پوشه وارد شده و دستور ذیل را صادر می‌کنیم:
 D:\vs-code-examples\sample01>code .
به این ترتیب کل پوشه‌ی sample01 در VS Code باز خواهد شد.


نصب VS Code بر روی Mac

نصب VS Code بر روی مک یا لینوکس نیز به همین ترتیب است و زمانیکه به آدرس فوق مراجعه می‌کنید، به صورت خودکار نوع سیستم عامل را تشخیص داده و بسته‌ی متناسبی را به شما پیشنهاد می‌کند. پس از دریافت بسته‌ی آن برای مک، یک application را دریافت خواهید کرد که آن‌را می‌توان به مجموعه‌ی Applications سیستم اضافه کرد. تنها تفاوت تجربه‌ی نصب آن با ویندوز، انتخاب گزینه‌ی Add to PATH آن است و به صورت پیش فرض نمی‌توان آن‌را از طریق ترمینال در هر مکانی اجرا کرد. برای این منظور، پس از اجرای اولیه‌ی VS Code، دکمه‌های Ctrl/Command+Shift+P را در VS Code فشرده و سپس path را جستجو کنید (در دستور یاد شده، Ctrl برای ویندوز و لینوکس است و Command برای Mac):


در اینجا گزینه‌ی install 'code' command path را انتخاب کنید تا بتوان VS Code را از طریق ترمینال نیز به سادگی اجرا کرد. به این ترتیب امکان اجرای دستور . code که بر روی ویندوز نیز ذکر شد، در اینجا نیز میسر خواهد بود.


نصب VS Code بر روی لینوکس

در اینجا نیز با مراجعه‌ی به آدرس https://code.visualstudio.com، بسته‌ی متناسب با لینوکس، جهت دریافت پیشنهاد خواهد شد؛ برای مثال بسته‌های deb. برای توزیع‌هایی مانند اوبونتو و یا rpm. برای ردهت. به علاوه اگر بر روی علامت ^ کنار بسته‌های دانلود کلیک کنید، یک بسته‌ی tar.gz. نیز قابل دریافت خواهد بود. تجربه‌ی نصب آن نیز همانند نمونه‌ی ویندوز است و Add to PATH آن به صورت خودکار انجام خواهد شد.


بررسی ابتدایی محیط VS Code

VS Code بر اساس فایل‌های قرار گرفته‌ی در یک پوشه و زیر پوشه‌های آن کار می‌کند. به همین جهت پس از صدور دستور . code، آن پوشه را در IDE خود نمایش خواهد داد. در اینجا برخلاف نگارش کامل ویژوال استودیو، روش کار، مبتنی بر یک فایل پروژه نیست و اگر خارج از VS Code نیز فایلی را به پوشه‌ی باز شده اضافه کنید، بلافاصله تشخیص داده شده و در اینجا لیست می‌شود. هرچند یک چنین تجربه‌ی کاربری با پروژه‌های ASP.NET Core نیز در نگارش‌های جدیدتر ویژوال استودیوی کامل، سعی شده‌است شبیه سازی شود؛ برخلاف سایر پروژه‌های ویژوال استودیو که اگر فایلی در فایل پروژه‌ی آن مدخلی نداشته باشد، به صورت پیش فرض نمایش داده نشده و درنظر گرفته نمی‌شود.

در ادامه برای نمونه از طریق منوی File->New File، یک فایل جدید را اضافه می‌کنیم. هرچند می‌توان اشاره‌گر ماوس را بر روی نام پوشه نیز برده و از دکمه‌های نوار ابزار آن نیز برای ایجاد یک فایل و یا پوشه‌ی جدید نیز استفاده کرد:


در اینجا فرمت ابتدایی فایل جدید را plain text تشخیص می‌دهد:


برای تغییر این حالت یا می‌توان فایل را ذخیره کرد و پسوند مناسبی را برای آن انتخاب نمود و یا در همان status bar پایین صفحه، بر روی plain text کلیک کنید تا منوی انتخاب زبان ظاهر شود:


به این ترتیب پیش از ذخیره‌ی فایل با پسوندی مناسب نیز می‌توان زبان مدنظر را تنظیم کرد. پس از آن، intellisense و syntax highlighting متناسب با آن زبان در دسترس خواهند بود.


بررسی تنظیمات VS Code

از طریق منوی File->Preferences->Settings می‌توان به تنظیمات VS Code دسترسی یافت.


در اینجا در سمت چپ، لیست تنظیمات مهیا و پیش فرض این محیط قرار دارند و در سمت راست می‌توان این پیش فرض‌ها را (پس از بررسی و جستجوی آن‌ها در پنل سمت چپ) بازنویسی و سفارشی سازی کرد.
تنظیمات انجام شده‌ی در اینجا را می‌توان به پوشه‌ی جاری نیز محدود کرد. برای این منظور بر روی لینک work space settings در کنار لینک user settings در تصویر فوق کلیک کنید. در این حالت یک فایل json را در پوشه‌ی vscode. نمای جاری VSCode، ایجاد خواهد کرد (sample01\.vscode\settings.json) که می‌تواند در برگیرنده‌ی تنظیمات سفارشی محدود و مختص به این پروژه و یا نما باشد.

یک نکته: تمام گزینه‌های منوی VS Code را و حتی مواردی را که در منوها لیست نشده‌اند، می‌توانید در Command Pallet آن با فشردن دکمه‌های Ctrl/Command+Shift+P نیز مشاهده کنید و به علاوه جستجوی آن نیز بسیار سریع‌تر است از دسترسی و کار مستقیم با منوها.


همچنین در اینجا اگر قصد یافتن سریع فایلی را داشته باشید، می‌توانید دکمه‌های Ctrl/Command+P را فشرده و سپس نام فایل را جستجو کرد:


این دو دستور، جزو دستورات پایه‌ای این IDE هستند و مدام از آن‌ها استفاده می‌شود.


نصب افزونه‌ی #C

اولین افزونه‌ای را که جهت کار با ASP.NET Core نیاز خواهیم داشت، افزونه‌ی #C است. برای این منظور در نوار ابزار عمودی سمت چپ صفحه، گزینه‌ی Extensions را انتخاب کنید:


در اینجا افزونه‌ی #C مایکروسافت را جستجو کرده و نصب کنید. نصب آن نیز بسیار ساده است. با حرکت اشاره‌گر ماوس بر روی آن، دکمه‌ی install ظاهر می‌شود یا حتی اگر آن‌را در لیست انتخاب کنیم، در سمت راست صفحه علاوه بر مشاهده‌ی جزئیات آن، دکمه‌های نصب و عزل نیز ظاهر خواهند شد.
تجربه‌ی کاربری محیط نصب افزونه‌های آن نیز نسبت به نگارش کامل ویژوال استودیو، بسیار بهتر است. برای نمونه اگر به تصویر فوق دقت کنید، در همینجا می‌توان جزئیات کامل افزونه، نویسنده یا نویسندگان آن و یا لیست تغییرات و وابستگی‌های آن‌را نیز بدون خروج از VSCode مشاهده و بررسی کرد. همچنین در دفعات بعدی اجرای VSCode، کار بررسی و نصب به روز رسانی‌های این افزونه‌ها نیز خودکار بوده و نیازی به بررسی دستی آن‌ها نیست.

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


در قسمت بعد، اولین پروژه‌ی ASP.NET Core خود را در VS Code ایجاد خواهیم کرد.
مطالب
اعتبار سنجی ورودی‌های کاربر در Kendo UI
در مطلب «فعال سازی عملیات CRUD در Kendo UI Grid» با نحوه‌ی تعریف مقدماتی اعتبارسنجی فیلدهای تعریف شده، آشنا شدید:
fields: {
      "Price": { type: "number", validation: { required: true, min: 1 } }
}
در ادامه نگاهی خواهیم داشت به جزئیات تکمیلی امکانات اعتبارسنجی ورودی‌های کاربر در Kendo UI.


Kendo UI Validation و HTML 5

در HTML 5 امکان تعریف نوع‌های خاص کنترل‌های ورودی کاربر مانند email، url، number، range، date، search و color وجود دارد. برای مثال در اینجا اگر کاربر تاریخ غیرمعتبری را وارد کند، مرورگر پیام اعتبارسنجی متناظری را به او نمایش خواهد داد. همچنین در HTML 5 امکان افزودن ویژگی required نیز به کنترل‌های ورودی پیش بینی شده‌است. اما باید درنظر داشت که مرورگرهای قدیمی از این امکانات پشتیبانی نمی‌کنند. در این حالت Kendo UI با تشویق استفاده از روش معرفی شده در HTML 5، با آن یکپارچه شده و همچنین این قابلیت‌های اعتبارسنجی HTML 5 را در مرورگرهای قدیمی نیز میسر می‌کند. Kendo UI Validation جزو نسخه‌ی سورس باز Kendo UI با مجوز Apache نیز می‌باشد.
نمونه‌ای از امکانات اعتبارسنجی توکار HTML 5 را در اینجا مشاهده می‌کنید:
<input type="text" name="firstName" required />
<input type="text" name="twitter" pattern="https?://(?:www\.)?twitter\.com/.+i" />
<input type="number" name="age" min="1" max="42" />
<input type="number" name="age" min="1" max="100" step="2" />
<input type="url" name="url" />
<input type="email" name="email" />


یکپارچه سازی اعتبارسنجی Kendo UI با اعتبارسنجی HTML 5

در اینجا یک فرم تشکیل شده با ساختار HTML 5 را ملاحظه می‌کنید. هر دو فیلد ورودی، با ویژگی استاندارد required مزین شده‌اند. همچنین توسط ویژگی type، ورودی دوم جهت دریافت آدرس ایمیل معرفی شده‌است.
چون فیلد دوم دارای دو اعتبارسنجی تعریف شده است، دارای دو ویژگی *-data برای تعریف پیام‌های اعتبارسنجی متناظر نیز می‌باشد. الگوی تعریف آن‌ها data-[rule]-msg است.
    <div class="k-rtl">
        <form id="testView">
            <label for="firstName">نام</label>
            <input id="firstName"
                   name="firstName"
                   type="text"
                   class="k-textbox"
                   required
                   validationmessage="لطفا نامی را وارد کنید">
            <br>
            <label for="emailId">آدرس پست الکترونیک</label>
            <input id="emailId"
                   name="emailId"
                   type="email"
                   dir="ltr"
                   required
                   class="k-textbox"
                   data-required-msg="لطفا ایمیلی را وارد کنید."
                   data-email-msg="ایمیل وارد شده معتبر نیست.">
            <br>
            <input type="submit" class="k-button" value="ارسال">
        </form>
    </div>

    <script type="text/javascript">
        $(function () {
            $("form#testView").kendoValidator();
        });
    </script>
تنها کاری که جهت یکپارچه سازی امکانات اعتبارسنجی Kendo UI با اعتبارسنجی استاندارد HTML 5 باید انجام داد، فراخوانی متد kendoValidator بر روی ناحیه‌ی مشخص شده است.



تعیین محل نمایش پیام‌های اعتبارسنجی

پیام‌های اعتبارسنجی Kendo UI به صورت خودکار در کنار فیلد متناظر با آن نمایش داده می‌شوند. اما اگر نیاز به تعیین مکان دستی آن‌ها وجود داشت (جهت خوانایی بهتر) باید به نحو ذیل عمل کرد:
     <input type="text" id="name" name="name" required>
     <span class="k-invalid-msg" data-for="name"></span>
در اینجا span با کلاس k-invalid-msg و ویژگی data-for که به name کنترل ورودی اشاره می‌کند، محل نمایش پیام اعتبارسنجی متناظر با فیلد name خواهد بود.


تعریف سراسری پیام‌های اعتبارسنجی

در مثال فوق، به ازای تک تک فیلدهای ورودی، پیام اعتبارسنجی متناظر با required وارد شد. می‌توان این پیام‌ها را حذف کرد و در قسمت messages متد kendoValidator قرار داد:
    <script type="text/javascript">
        $(function () {
            $("form#testView").kendoValidator({
                messages: {
                    // {0} would be replaced with the input element's name
                    required: '{0} را تکمیل کنید.',
                    email: 'ایمیل وارد شده معتبر نیست.'
                }
            });
        });
    </script>
- به این صورت پیام‌های اعتبارسنجی required و email، به صورت یکسانی به تمام المان‌های دارای این ویژگی‌ها اعمال خواهند شد.
- در این پیام‌ها {0} با مقدار ویژگی name فیلد ورودی متناظر جایگزین می‌شود.
- اگر هم در markup و هم در تعاریف kendoValidator، پیام‌های اعتبارسنجی تعریف شوند، حق تقدم با تعاریف markup خواهد بود.


اعتبارسنجی سفارشی سمت کاربر

علاوه بر امکانات استاندارد HTML 5، امکان تعریف دستورهای اعتبارسنجی سفارشی نیز وجود دارد:
    <script type="text/javascript">
        $(function () {
            $("form#testView").kendoValidator({
                rules: {
                    customRule1: function (input) {
                        if (!input.is("[id=firstName]"))
                            return true;

                        var re = /^[A-Za-z]+$/;
                        return re.test(input.val());
                    }
                   //, customRule1: ….
                },
                messages: {
                    // {0} would be replaced with the input element's name
                    required: '{0} را تکمیل کنید.',
                    email: 'ایمیل وارد شده معتبر نیست.',
                    customRule1: 'اعداد مجاز نیستند.'
                }
            });
        });
    </script>


- همانطور که ملاحظه می‌کنید، برای تعریف منطق اعتبارسنجی سفارشی، باید از خاصیت rules ورودی متد kendoValidator شروع کرد. در اینجا نام یک متد callback دلخواهی را وارد کرده و سپس بر اساس منطق اعتبارسنجی مورد نظر، باید true/false را بازگشت داد. برای نمونه در این مثال اگر کاربر در فیلد نام، عدد وارد کند، ورودی او مورد قبول واقع نخواهد شد.
- باید دقت داشت که اگر بررسی input.is صورت نگیرد، منطق تعریف شده به تمام کنترل‌های صفحه اعمال می‌شود.
- پیام متناظر با این دستور سفارشی جدید، در قسمت messages، دقیقا بر اساس نام callback method تعریف شده در قسمت rules باید تعریف شود.


فراخوانی دستی اعتبارسنجی یک فرم

در حالت پیش فرض، با کلیک بر روی دکمه‌ی ارسال، اعتبارسنجی کلیه عناصر فرم به صورت خودکار انجام می‌شود. اگر بخواهیم در این بین یک پیام سفارشی را نیز نمایش دهیم می‌توان به صورت زیر عمل کرد:
    <script type="text/javascript">
        $(function () {
            $("form#testView").submit(function (event) {
                event.preventDefault();
                var validator = $("form#testView").data("kendoValidator");
                if (validator.validate()) {
                    alert("validated!");
                } else {
                    alert("There is invalid data in the form.");
                }
            });

            $("form#testView").kendoValidator();
        });
    </script>
در اینجا رخداد submit فرم بازنویسی شده و متد validate آن بر اساس kendoValidator تعریف شده، به صورت دستی فراخوانی می‌شود.



اعتبارسنجی سفارشی در DataSource

در تعریف فیلدهای مدل DataSource، امکان تعریف اعتبارسنجی‌های پیش فرضی مانند rquired، min، max و امثال آن وجود دارد که نمونه‌ای از آن‌را در بحث فعال سازی CRUD در Kendo UI Grid مشاهده کردید:
fields: {
   "serviceName": { 
    type: "string", 
    defaultValue: "Inspection",
    editable: true, 
    nullable: false, 
    validation: { /*...*/ }
   },
   // ...
}
برای تعریف اعتبارسنجی سفارشی در اینجا، همانند متد kendoValidator نیاز است یک یا چند callback متد سفارشی را طراحی کرد:
  schema: {
            model: {
                         id: "ProductID",
                         fields: {
                                        ProductID: { editable: false, nullable: true },
                                        ProductName: {
                                            validation: {
                                                required: true,
                                                custom1: function (input) {
                                                    if (input.is("[name='ProductName']") && input.val() != "") {
                                                        input.attr("data-custom1-msg", "نام محصول باید با حرف بزرگ انگلیسی شروع شود");
                                                        return /^[A-Z]/.test(input.val());
                                                    }

                                                    return true;
                                                }
                                              // ,custom2: ...
                                            }
                                        },
                                        UnitPrice: { type: "number", validation: { required: true, min: 1} },
                                        Discontinued: { type: "boolean" },
                                        UnitsInStock: { type: "number", validation: { min: 0, required: true} }
                                    }
                                }
                            }
نام این متد که نهایتا true/false بر می‌گرداند، اختیاری است. نام کنترل جاری همان نام فیلد متناظر است (جهت محدود کردن بازه‌ی اعمال منطق اعتبارسنجی). برای مقدار دهی پیام اعتبارسنجی از متد input.attr و الگوی data-[validationRuleName]-msg استفاده می‌شود. ضمنا به هر تعداد لازم می‌توان در اینجا custom rule تعریف کرد.
متد ()input.val مقدار کنترل جاری را بر می‌گرداند. برای دسترسی به مقدار سایر کنترل‌ها می‌توان از روش ()fieldName").val#")$ استفاده کرد.
مطالب
توسعه اپلیکیشن‌های Node.js در ویژوال استودیو
آشنایی با Node.js

Node.js یک پلت‌فرم جاوا اسکریپتی سمت سرور است که ابتدا توسط Ryan Dahl در سال 2009 معرفی گردید. از Node.js جهت ساخت اپلیکیشن‌های مقیاس‌پذیر تحت شبکه و با زبان برنامه‌نویسی جاوا اسکریپت در سمت سرور استفاده می‌شود. Node.js در پشت صحنه از ران‌تایم V8 استفاده می‌کند؛ یعنی همان ران‌تایمی که درون مرورگر کروم استفاده شده است. Node.js در واقع یک wrapper برای این موتور V8 است؛ جهت ارائه‌ی قابلیت‌های بیشتری برای ایجاد برنامه‌های تحت شبکه. یکی از مزایای Node.js سریع بودن آن است. دلیل آن نیز این است که به صورت کامل توسط کدهای C تهیه شده است (البته می‌توانید در این آدرس benchmark مربوط به ASP.NET Core 1.0 و مقایسه‌ی آن با دیگر پلت‌فرم‌ها را نیز بررسی کنید).

چه نوع اپلیکیشن‌های را می‌توان با Node.js توسعه داد؟

  • سرور WebSocket جهت توسعه‌ی اپلیکیشن‌های بلادرنگ
  • فایل آپلودر سریع در سمت کلاینت
  • Ad Server
  • و ...
لازم به ذکر است، Node.js یک فریم‌ورک تحت وب نیست و همچنین قرار نیست یک جایگزین برای دیگر فریم‌ورک‌ها مانند ASP.NET MVC و... باشد. در حالت کلی هدف آن انجام یک‌سری اعمال سطح پائین شبکه‌ایی است. البته کتابخانه‌هایی برفراز Node.js نوشته شده‌اند که آن را تبدیل به یک وب‌فریم‌ورک خواهند کرد (+).
قبل از شروع به کار با Node.js، باید تفاوت blocking code و non-blocking code را بدانید. فرض کنید قرار است محتویات یک فایل را در خروجی نمایش دهیم. در حالت اول یعنی blocking code، باید ابتدا فایل را از فایل سیستم بخوانیم و آن را به یک متغیر انتساب دهیم و در نهایت محتویات متغیر را در خروجی چاپ کنیم. در این‌حالت تا زمانیکه فایل از فایل سیستم خوانده نشود، نمی‌توان محتویات آن را در خروجی نمایش داد. اما در حالت non-blocking فایل را از فایل سیستم می‌خوانیم و هر زمانیکه عملیات خواندن فایل به اتمام رسید می‌توانیم محتویات فایل را در خروجی نمایش دهیم. یعنی برخلاف حالت قبل، در این روش بلاک شدن کد به ازای عملیات زمانبر را نخواهیم داشت. در این روش عبارت هر زمانیکه عملیات خواندن فایل به اتمام رسید یک callback تلقی می‌شود. یعنی تا وقتیکه فایل از فایل سیستم خوانده شود، به دیگر عملیات رسیدگی خواهیم کرد و به محض اتمام خوانده شدن فایل، عملیات نمایش در خروجی را فراخوانی خواهیم کرد. در نتیجه برای حالت blocking این چنین کدی را خواهیم داشت:
var contents = fs.readFileSync('filePath');
console.log(content);
console.log('Doing something else');
همچنین برای حالت non-blocking نیز این چنین کدی را خواهیم داشت:
fs.readFile('filePath', function (err, contents) {
   console.log(contents);
});
console.log('Doing something else');

برای شروع به کار با Node.js می‌توانید با مراجعه به وب‌سایت رسمی آن، آن‌را دانلود و بر روی سیستم خود نصب کنید. بعد از نصب Node می‌توانیم از طریق command line وارد shell آن شوید و دستورات جاوا اسکریپتی خود را اجرا نمائید:


احتمالاً به این نوع استفاده‌ی از Node.js که به REPL معروف است، نیازی نداشته باشید. در واقع هدف بررسی نصب بودن ران‌تایم بر روی سیستم است. با استفاده از فرمان node نیز می‌توان یک فایل جاوا اسکریپتی را اجرا کرد. برای اینکار یک فایل با نام test.js را با محتویات زیر درون VS Code ایجاد کنید:



سپس دستور node test.js را وارد کنید:



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


استفاده از Node.js در ویژوال استودیو


برای کار با Node.js درون ویژوال استودیو باید ابتدا افزونه‌ی Node.js Tools را برای ویژوال استودیو نصب کنید. بعد از نصب این افزونه‌، تمپلیت Node.js در زمان ایجاد یک پروژه برای شما نمایش داده خواهد شد:

 


برای شروع، تمپلیت Blank Node.js Console Application را انتخاب کرده و بر روی OK کلیک کنید. با اینکار یک پروژه با ساختار زیر برایمان ایجاد خواهد شد: 



همانطور که ملاحظه می‌کنید، یک فایل با نام app.js درون تمپلیت ایجاد شده، موجود است. app.js در واقع نقطه‌ی شروع برنامه‌‌مان خواهد بود. همچنین دو فایل دیگر نیز با نام‌های README.md، جهت افزودن توضیحات و یک فایل با نام package.json، جهت مدیریت وابستگی‌های برنامه به پروژه اضافه شده‌اند. اکنون می‌توانیم شروع به توسعه‌ی برنامه‌ی خود درون ویژوال استودیو کنیم. همچنین می‌توانیم از قابلیت‌های debugging ویژوال استودیو نیز بهره ببریم: 



اگر مسیر پروژه‌ی ایجاد شده‌ی فوق را درون windows explorer باز کنید خواهید دید که ساختار آن شبیه به یک پروژه‌ی Node.js می‌باشد. با این تفاوت که دو آیتم دیگر همانند دیگر پروژه‌های ویژوال استودیو نیز به آن اضافه شده است که طبیعتاً می‌توانید در حین کار با سورس کنترل، از انتشار آنها صرفنظر کنید.



لازم به ذکر است پروژه‌ی ایجاد شده‌ی فوق را نیز می‌توانید همانند حالت عادی، از طریق command line و همانند پروژه‌های Node.js اجرا کنید: 

node app.js


در واقع از ویژوال استدیو می‌توانیم به عنوان یک ابزار برای دیباگ پروژه‌های Node.js استفاده کنیم. لازم به ذکر است، Visual Studio Code نیز امکان دیباگ اپلیکیشن‌های Node.js را در اختیارمان قرار می‌دهد. در نتیجه در مواقعیکه نسخه‌ی کامل ویژوال استودیو در دسترس نیست نیز می‌توانیم از VS Code برای دیباگ برنامه‌هایمان استفاده کنیم:


مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت پنجم - بررسی چرخه‌ی حیات کامپوننت‌ها
در قسمت قبل، نگاهی داشتیم به 4 نوع مختلف data binding در AngularJS 2.0. در قسمت جاری می‌خواهیم کیفیت کدهای کامپوننت لیست محصولات را با strong typing بهبود بخشیده و همچنین چرخه‌ی حیات کامپوننت‌ها را به همراه ایجاد custom pipes بررسی کنیم.


افزودن strong typing به کامپوننت نمایش لیست محصولات

یکی از مزایای کار با TypeScript امکان انتساب نوع‌های مشخص یا سفارشی، به متغیرها و اشیاء تعریف شده‌است. برای مثال تاکنون هر خاصیت تعریف شده‌ای دارای نوع است. اما هنوز نوعی را برای آرایه‌ی محصولات تعریف نکرده‌ایم و نوع آن، نوع پیش فرض any است که برخلاف رویه‌ی متداول کار با TypeScript است.
برای تعریف نوع‌های سفارشی می‌توان از اینترفیس‌های TypeScript استفاده کرد. یک اینترفیس، قراردادی است که نحوه‌ی تعریف تعدادی خاصیت و متد به هم مرتبط را مشخص می‌کند. سپس کلاس‌های مختلف می‌توانند با پیاده سازی این اینترفیس، قرارداد تعریف شده‌ی در آن را عملی کنند. همچنین از اینترفیس‌ها می‌توان به عنوان یک data type جدید نیز استفاده کرد. البته ES 5 و ES 6 از اینترفیس‌ها پشتیبانی نمی‌کنند و تعریف آن‌ها در TypeScript صرفا جهت کمک به کامپایلر، برای یافتن خطاها، پیش از اجرای برنامه است و به کدهای جاوا اسکریپتی معادلی ترجمه نمی‌شوند.

در ادامه برای تکمیل مثال این سری، فایل جدید App\products\product.ts را به پروژه اضافه کنید؛ با این محتوا:
export interface IProduct {
    productId: number;
    productName: string;
    productCode: string;
    releaseDate: string;
    price: number;
    description: string;
    starRating: number;
    imageUrl: string;
}
یک اینترفیس، با واژه‌ی کلیدی interface تعریف شده و سپس نام آن تعریف می‌شود. مرسوم است این نام با I شروع شود؛ هرچند الزامی نیست و در بسیاری از اینترفیس‌های AngularJS 2.0 از این روش نامگذاری استفاده نشده‌است.
همچنین از آنجائیکه این اینترفیس را در یک فایل ts مجزا قرار داده‌ایم، برای اینکه بتوان از آن در سایر قسمت‌های برنامه استفاده کرد، نیاز است در ابتدای آن، واژه‌ی کلیدی export را نیز ذکر کرد.

پس از تعریف این اینترفیس، برای استفاده از آن به عنوان یک data type جدید، ابتدا ماژول آن import خواهد شد و سپس از نام آن به عنوان نوع داده‌ی جدیدی، استفاده می‌شود. برای این منظور فایل product-list.component.ts را گشوده و تغییرات ذیل را به آن اعمال کنید:
import { Component } from 'angular2/core';
import { IProduct } from './product';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html'
})
export class ProductListComponent {
    // as before ...

    products: IProduct[] = [
        // as before ...
    ]; 

    // as before ...
}
در اینجا ابتدا IProduct را import و سپس بجای any، نوع جدید IProduct را جهت مشخص سازی نوع آرایه‌ی products تعریف کرده‌ایم.
مزیت اینکار این است که برای مثال در اینجا اگر در لیست اعضای آرایه‌ی products، نام خاصیتی اشتباه تایپ شده باشد یا حتی بجای عدد، از رشته استفاده شده باشد، بلافاصله در ادیتور مورد استفاده، خطای مرتبط گوشزد شده و همچنین این فایل دیگر کامپایل نخواهد شد. به علاوه اینبار برای تعریف خواص اعضای آرایه‌ی products، ادیتور مورد استفاده، intellisense را نیز دراختیار ما قرار می‌دهد و کاملا مشخص است که چه اعضایی مدنظر هستند و نوع آن‌ها چیست.



مدیریت cssهای هر کامپوننت به صورت مجزا

هنگام ساخت یک قالب یا template، در بسیاری از اوقات نیاز است css مرتبط با آن نیز، منحصر به همان قالب بوده و نشتی نداشته باشد. برای مثال زمانیکه یک کامپوننت را درون کامپوننتی دیگر قرار می‌دهیم، باید css آن نیز در دسترس قرار بگیرد و css فعلی کامپوننت دربرگیرنده را بازنویسی نکند. روش‌‌های مختلفی برای مدیریت این مساله وجود دارند:
الف) تعریف شیوه نامه‌ها به صورت inline داخل خود قالب‌ها. این حالت، مشکلات نگهداری و استفاده‌ی مجدد را دارد.
ب) تعریف شیوه نامه‌ها در یک فایل خارجی css و سپس لینک کردن آن به صفحه‌ای اصلی یا index.html
در این حالت به ازای هر فایل، یکبار باید این تعریف در صفحه‌ای اصلی سایت صورت گیرد. همچنین این فایل‌ها می‌توانند مقادیر یکدیگر را بازنویسی کرده و بر روی هم تاثیر بگذارند.
ج) تعریف شیوه نامه‌ها به همراه تعریف کامپوننت. این روشی است که توسط AngularJS توصیه شده‌است و نگهداری و مقیاس پذیری آن ساده‌تر است.
تزئین کننده‌ی Component به همراه دو خاصیت دیگر به نام‌های styles و stylesUrl نیز می‌باشد.
در حالت استفاده از خاصیت styles، شیوه‌نامه‌ی متناظر با کامپوننت، در همانجا به صورت inline تعریف می‌شود:
 @Component({
    //...
    styles: ['thead {color: blue;}']
})
همانطور که مشاهده می‌کنید، خاصیت styles به صورت یک آرایه تعریف شده‌است و امکان پذیرش چندین شیوه نامه‌ی جدا شده‌ی با کاما را دارد.
روش بهتر، استفاده از خاصیت styleUrls است که در آن می‌توان مسیر یک یا چند فایل css را مشخص کرد:
 @Component({
     //...
     styleUrls: ['app/products/product-list.component.css']
})
این خاصیت نیز یک آرایه را می‌پذیرد و در اینجا می‌توان مسیر چندین فایل css را در صورت نیاز ذکر کرد.

برای آزمایش آن فایل جدید product-list.component.css را به پوشه‌ی products مثال این سری اضافه کنید؛ با این محتوا:
thead {
  color: #337AB7;
}
سپس لینک این فایل را به مجموعه خواص کامپوننت لیست محصولات (product-list.component.ts) اضافه می‌کنیم:
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html',
    styleUrls: ['app/products/product-list.component.css']
})
export class ProductListComponent {
   //...
در این حالت اگر برنامه را اجرا کنید، رنگ سرستون‌های جدول محصولات به نحو ذیل تغییر کرده‌اند:


یک نکته

شیوه نامه‌ای که به این صورت توسط AngularJS 2.0 اضافه می‌شود، با سایر شیوه نامه‌های موجود تداخل نخواهد کرد. علت آن‌را در تصویر ذیل که با استفاده از developer tools مرورگرها قابل بررسی است، می‌توان مشاهده کرد:


در اینجا AngularJS 2.0، با ایجاد ویژگی‌های سفارشی خودکار (attributes) میدان دید css را کنترل می‌کند. به این ترتیب شیوه نامه‌ی کامپوننت یک، که درون کامپوننت دو قرار گرفته‌است، نشتی نداشته و بر روی سایر قسمت‌های صفحه تاثیری نخواهد گذاشت؛ برخلاف شیوه نامه‌هایی که به صورت متداولی به صفحه‌ی اصلی سایت لینک شده‌‌اند.


بررسی چرخه‌ی حیات کامپوننت‌ها

هر کامپوننت دارای چرخه‌ی حیاتی است که توسط AngularJS 2.0 مدیریت می‌شود و شامل مراحلی مانند ایجاد، رندر، ایجاد و رندر فرزندان آن، پردازش تغییرات آن و در نهایت تخریب آن کامپوننت می‌شود. برای اینکه بتوان با برنامه نویسی به این مراحل چرخه‌ی حیات یک کامپوننت دسترسی یافت، تعدادی life cycle hook طراحی شده‌اند. سه مورد از مهم‌ترین life cycle hooks شامل موارد ذیل هستند:
الف) OnInit: از این hook برای انجام کارهای آغازین یک کامپوننت مانند دریافت اطلاعات از سرور، استفاده می‌شود.
ب) OnChanges: از آن جهت انجام اعمالی پس از تغییرات input properties استفاده می‌شود.
خواص ورودی و همچنین کار با سرور را در قسمت‌های بعدی بررسی خواهیم کرد.
ج) OnDestroy: از آن جهت پاکسازی منابع اختصاص داده شده استفاده می‌شود.

برای استفاده‌ی از این hookها، نیاز است اینترفیس آن‌ها را پیاده سازی کنیم. از آنجائیکه AngularJS 2.0 نیز با TypeScript نوشته شده‌است، به همراه تعدادی اینترفیس از پیش تعریف شده می‌باشد. برای مثال به ازای هر life cycle hook، یک اینترفیس تعریف شده در آن وجود دارد. برای نمونه اینترفیس hook ایی به نام OnInit، دقیقا همان OnInit، نام دارد (و با I شروع نشده‌است):
 export class ProductListComponent implements OnInit {
پس از ذکر implements OnInit در انتهای تعریف کلاس، اکنون باید ماژول مرتبط با آن نیز جهت شناسایی این اینترفیس import شود:
 import { Component, OnInit } from 'angular2/core';
و دست آخر متد ngOnInit تعریف شده‌ی در این اینترفیس باید توسط کلاس پیاده سازی کننده‌ی آن تامین شود:
ngOnInit(): void {
    console.log('In OnInit');
}
نام این متدها عموما شروع شده با ng و ختم شده به نام اینترفیس hook متناظر هستند؛ مانند ngOnInit فوق.

به عنوان تمرین، فایل product-list.component.ts را گشوده و سه مرحله‌ی implements سپس import و در آخر تعریف متد ngOnInit فوق را به آن اضافه کنید.
در ادامه برنامه را اجرا کرده و به کنسول developer tools مرورگر خود جهت مشاهده‌ی console.log فوق مراجعه کنید:



ساخت یک Pipe سفارشی جهت فعال سازی textbox فیلتر کردن محصولات

همانطور که در قسمت قبل نیز عنوان شد، کار pipes، تغییر اطلاعات حاصل از data binding، پیش از نمایش آن‌ها در رابط کاربری است و AngularJS 2.0 به همراه تعدادی pipe توکار است؛ مانند currency، percent و غیره. در ادامه قصد داریم یک pipe سفارشی را ایجاد کنیم تا بر روی حلقه‌ی ngFor* نمایش لیست محصولات تاثیرگذار شود و همچنین ورودی خود را از مقدار وارد شده‌ی توسط کاربر دریافت کند.
برای این منظور، یک فایل جدید را به نام product-filter.pipe.ts به پوشه‌ی products اضافه کنید. سپس کدهای آن‌را به نحو ذیل تغییر دهید:
import { PipeTransform, Pipe } from 'angular2/core';
import { IProduct } from './product';
 
@Pipe({
    name: 'productFilter'
})
export class ProductFilterPipe implements PipeTransform {
 
    transform(value: IProduct[], args: string[]): IProduct[] {
        let filter: string = args[0] ? args[0].toLocaleLowerCase() : null;
        return filter ? value.filter((product: IProduct) =>
            product.productName.toLocaleLowerCase().indexOf(filter) != -1) : value;
    }
}
برای تعریف یک pipe سفارشی جدید، کار با پیاده سازی اینترفیس PipeTransform شروع می‌شود. این اینترفیس دارای متدی است به نام transform که امضای آن‌را در کدهای فوق ملاحظه می‌کنید. کار آن اعمال تغییرات بر روی value دریافتی و سپس بازگشت آن است. بنابراین اولین پارامتر آن، مقادیر اصلی را که قرار است تغییر کنند، مشخص می‌کند. در اینجا نوع آن‌را از نوع اینترفیسی که در ابتدای بحث تعریف کردیم، تعیین کرده‌ایم. پارامتر دوم آن، لیست پارامترها و آرگومان‌های اختیاری این فیلتر را مشخص می‌کند.
برای مثال در اینجا می‌خواهیم شرایط فیلتر محصولات وارد شده‌ی توسط کاربر را دریافت کنیم.
خروجی این متد نیز از نوع آرایه‌ای از IProduct تعریف شده‌است؛ از این جهت که نتیجه نهایی فیلتر اطلاعات نیز آرایه‌ای از همین نوع است. کار این pipe پیاده سازی متد contains به صورت غیرحساس به کوچکی و بزرگی حروف است.
سپس بلافاصله بالای نام این کلاس، از یک decorator جدید به نام Pipe استفاده شده‌است تا به AngularJS 2.0 اعلام شود، این کلاس، صرفا یک کلاس معمولی نیست و یک Pipe است.
در ابتدای فایل هم importهای لازم جهت تعریف اینترفیس‌های مورد استفاده‌ی در این ماژول، ذکر شده‌اند.

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

پس از تعریف این pipe سفارشی، برای استفاده‌ی از آن در یک template، به فایل product-list.component.html مراجعه کرده و سپس ngFor* آن‌را به نحو ذیل تغییر می‌دهیم:
 <tr *ngFor='#product of products | productFilter:listFilter'>
همانطور که ملاحظه می‌کنید، نام این pipe جدید که در decorator مرتبط با آن، توسط خاصیت name مشخص گردیده‌است، ذکر شده‌است. پس از آن یک : قرار گرفته‌است که مشخص کننده‌ی پارامتر اول این pipe است که در اینجا خاصیت listFilter تعریف شده‌ی در قسمت قبل را به آن انتساب داده‌ایم.
اگر از قسمت قبل به خاطر داشته باشید، این خاصیت را توسط two-way binding به روز می‌کنیم (اطلاعات وارد شده‌ی در textbox، بلافاصله به این خاصیت منعکس می‌شوند و برعکس):
 <input type='text'  [(ngModel)]='listFilter' />
تا اینجا این pipe را در قالب لیست محصولات بکار بردیم؛ اما کامپوننت آن نمی‌داند که این pipe را باید از کجا تامین کند. به همین جهت فایل product-list.component.ts را گشوده و خاصیت pipes را به نحو ذیل مقدار دهی کنید:
import { Component, OnInit } from 'angular2/core';
import { IProduct } from './product';
import { ProductFilterPipe } from './product-filter.pipe';
 
@Component({
    selector: 'pm-products',
    templateUrl: 'app/products/product-list.component.html',
    styleUrls: ['app/products/product-list.component.css'],
    pipes: [ProductFilterPipe]
})
export class ProductListComponent implements OnInit {
   //...
در اینجا دو کار صورت گرفته‌است. ابتدا افزودن pipe جدید ProductFilterPipe به لیست خاصیت pipes کامپوننت و سپس import ماژول آن درابتدای فایل.

اکنون اگر برنامه را اجرا کنید، خروجی ذیل را مشاهده خواهید کرد:


در اینجا چون مقدار فیلتر وارد شده‌ی پیش فرض، cart است، فقط ردیف Garden Cart نمایش داده شده‌است. اگر این مقدار را خالی کنیم، تمام ردیف‌ها نمایش داده می‌شوند و اگر برای مثال ham را جستجو کنیم، فقط ردیف Hammer نمایش داده می‌شود.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MVC5Angular2.part5.zip


خلاصه‌ی بحث

- اینترفیس‌ها یکی از روش‌های بهبود strong typing برنامه‌های AngularJS 2.0 هستند.
- جهت مدیریت بهتر شیوه‌نامه‌های هر کامپوننت بهتر است از روش styleUrls استفاده شود تا از نشتی‌های تعاریف شیوه‌نامه‌ها جلوگیری گردد.
- از life cycle hooks برای مدیریت رخدادهای مرتبط با طول عمر یک کامپوننت استفاده می‌شود؛ برای مثال دریافت اطلاعات از سرور و یا پاکسازی منابع مصرفی.
- تعریف یک pipe سفارشی با پیاده سازی اینترفیس PipeTransform انجام می‌شود. سپس نام این Pipe، به قالب مدنظر اضافه شده و در ادامه نیاز است کامپوننت استفاده کننده‌ی از این قالب را نیز از وجود این Pipe مطلع کرد.
مطالب
بررسی Source Generators در #C - قسمت دوم - یک مثال
یک مثال: پیاده سازی INotifyPropertyChanged توسط Source Generators

هدف از اینترفیس INotifyPropertyChanged که به همراه یک رخ‌داد است:
public interface INotifyPropertyChanged  
{ 
   event PropertyChangedEventHandler PropertyChanged;  
}
مطلع سازی استفاده کننده‌ی از یک شیء، از تغییرات رخ‌داده‌ی در مقادیر خواص آن است که نمونه‌ی آن، در برنامه‌های WPF، جهت به روز رسانی UI، زیاد مورد استفاده قرار می‌گیرد. البته این رخ‌داد به خودی خود کار خاصی را انجام نمی‌دهد و برای استفاده‌ی از آن، باید مقدار زیادی کد نوشت و این مقدار کد نیز باید به ازای تک تک خواص یک کلاس مدل، تکرار شوند:
  partial class CarModel : INotifyPropertyChanged
  {

    private double _speedKmPerHour;
    
    public double SpeedKmPerHour
    {
      get => _speedKmPerHour;
      set
      {
        _speedKmPerHour = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SpeedKmPerHour)));
      }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
  }
همچنین باید درنظر داشت که با تغییر نام خاصیتی، میزان قابل ملاحظه‌ای از این کدهای تکراری نیز باید به روز رسانی شوند که این عملیات می‌تواند ایده‌ی خوبی برای استفاده‌ی از Source Generators باشد.
اگر بخواهیم تولید این کدهای تکراری را به Source Generators محول کنیم، می‌توان برای مثال فیلد خصوصی مرتبط را نگه داشت و تولید مابقی کدها را خودکار کرد:
  partial class CarModel : INotifyPropertyChanged
  {
    private double _speedKmPerHour;    
  }
در این حالت کلاس مدل، به صورت partial تعریف می‌شود و فقط فیلد خصوصی، در کدهای ما حضور خواهد داشت. مابقی کدهای این کلاس partial به صورت خودکار توسط یک Source Generator سفارشی تولید خواهد شد. همانطور که ملاحظه می‌کنید، کاهش حجم قابل ملاحظه‌ای حاصل شده و همچنین اگر فیلد خصوصی دیگری نیز در اینجا اضافه شود، واکنش Source Generator ما آنی خواهد بود و بلافاصله کدهای مرتبط را تولید می‌کند و برنامه، بدون مشکلی کامپایل خواهد شد؛ هرچند به ظاهر INotifyPropertyChanged ذکر شده، در این کلاس اصلا پیاده سازی نشده‌است.


ایجاد پروژه‌ی Source Generator

در ابتدا برای ایجاد تولید کننده‌ی خودکار کدهای INotifyPropertyChanged، یک class library را به solution جاری اضافه می‌کنیم. سپس نیاز است ارجاعاتی را به دو بسته‌ی نیوگت زیر نیز افزود:
<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.2.0" PrivateAssets="all" />
  </ItemGroup>
</Project>
سپس کلاس جدید NotifyPropertyChangedGenerator را به نحو زیر به آن اضافه می‌کنیم:
  [Generator]
  public class NotifyPropertyChangedGenerator : ISourceGenerator
  {
    public void Initialize(GeneratorInitializationContext context)
    {
    }

    public void Execute(GeneratorExecutionContext context)
    {
- این کلاس باید اینترفیس ISourceGenerator را پیاده سازی کرده و همچنین مزین به ویژگی Generator باشد.
- اینترفیس ISourceGenerator به همراه دو متد Initialize و Execute است که در صورت نیاز باید پیاده سازی شوند.

در متد Execute، به خاصیت context.Compilation دسترسی داریم. این خاصیت تمام اطلاعاتی را که کامپایلر از Solution جاری در اختیار دارد، به توسعه دهنده ارائه می‌دهد. برای نمونه پیاده سازی متد Execute تولید کننده‌ی کد مثال جاری، چنین شکلی را دارد:
    public void Execute(GeneratorExecutionContext context)
    {
      // uncomment to debug the actual build of the target project
      // Debugger.Launch();
      var compilation = context.Compilation;
      var notifyInterface = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged");

      foreach (var syntaxTree in compilation.SyntaxTrees)
      {
        var semanticModel = compilation.GetSemanticModel(syntaxTree);
        var immutableHashSet = syntaxTree.GetRoot()
          .DescendantNodesAndSelf()
          .OfType<ClassDeclarationSyntax>()
          .Select(x => semanticModel.GetDeclaredSymbol(x))
          .OfType<ITypeSymbol>()
          .Where(x => x.Interfaces.Contains(notifyInterface))
          .ToImmutableHashSet();

        foreach (var typeSymbol in immutableHashSet)
        {
          var source = GeneratePropertyChanged(typeSymbol);
          context.AddSource($"{typeSymbol.Name}.Notify.cs", source);
        }
      }
    }
در اینجا با استفاده از context.Compilation به اطلاعات کامپایلر دسترسی پیدا کرده و سپس SyntaxTrees آن‌را یکی یکی، جهت یافتن کلاس‌ها و یا همان ClassDeclarationSyntax ها، پیمایش و بررسی می‌کنیم. سپس از بین این کلاس‌ها، کلاس‌هایی که INotifyPropertyChanged را پیاده سازی کرده باشند، انتخاب می‌کنیم که اطلاعات آن در پایان کار، به متد GeneratePropertyChanged جهت تولید مابقی کدهای partial class ارسال شده و کد تولیدی، به context اضافه می‌شود تا به نحو متداولی همانند سایر کدهای برنامه، به مجموعه کدهای مورد بررسی کامپایلر اضافه شود.

نکته‌ی مهم و جالب در اینجا این است که نیازی نیست تا قطعه کد جدید را به صورت SyntaxTrees در آورد و به کامپایلر اضافه کرد. می‌توان این قطعه کد را به نحو متداولی، به صورت یک قطعه رشته‌ی استاندارد #C، تولید و به کامپایلر با متد context.AddSource ارائه کرد که نمونه‌ای از آن‌را در ذیل مشاهده می‌کنید:
    private string GeneratePropertyChanged(ITypeSymbol typeSymbol)
    {
      return $@"
using System.ComponentModel;

namespace {typeSymbol.ContainingNamespace}
{{
  partial class {typeSymbol.Name}
  {{
    {GenerateProperties(typeSymbol)}
    public event PropertyChangedEventHandler? PropertyChanged;
  }}
}}";
    }

    private static string GenerateProperties(ITypeSymbol typeSymbol)
    {
      var sb = new StringBuilder();
      var suffix = "BackingField";

      foreach (var fieldSymbol in typeSymbol.GetMembers().OfType<IFieldSymbol>()
        .Where(x=>x.Name.EndsWith(suffix)))
      {
        var propertyName = fieldSymbol.Name[..^suffix.Length];
        sb.AppendLine($@"
    public {fieldSymbol.Type} {propertyName}
    {{
      get => {fieldSymbol.Name};
      set
      {{
        {fieldSymbol.Name} = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof({propertyName})));
      }}
    }}");
      }

      return sb.ToString();
    }
در اینجا در ابتدا بدنه‌ی کلاس partial تکمیل می‌شود. سپس خواص عمومی آن بر اساس فیلدهای خصوصی تعریف شده، تکمیل می‌شوند. در این مثال اگر یک فیلد خصوصی به عبارت BackingField ختم شود، به عنوان فیلدی که قرار است معادل کدهای INotifyPropertyChanged را داشته باشد، شناسایی می‌شود و به همراه کدهای تولید شده‌ی خودکار خواهد بود.

کدهای source generator ما همین مقدار بیش‌تر نیست. اکنون می‌خواهیم از آن در یک برنامه‌ی کنسول جدید (برای مثال به نام NotifyPropertyChangedGenerator.Demo) استفاده کنیم. برای اینکار نیاز است ارجاعی را به آن اضافه کنیم؛ اما این ارجاع، یک ارجاع متداول نیست و نیاز به ذکر چنین ویژگی خاصی وجود دارد:
<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <ProjectReference Include="..\NotifyPropertyChangedGenerator\NotifyPropertyChangedGenerator.csproj"
                      OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
  </ItemGroup>
</Project>
در اینجا میسر دهی پروژه‌ی تولید کننده‌ی کد، همانند سایر پروژه‌ها است؛ اما نوع آن باید آنالایزر معرفی شود. به همین جهت از خاصیت OutputItemType با مقدار Analyzer استفاده شده‌است. همچنین تنظیم ReferenceOutputAssembly به false به این معنا است که این اسمبلی ویژه، یک وابستگی و dependency واقعی پروژه‌ی جاری نیست و ما قرار نیست به صورت مستقیمی از کدهای آن استفاده کنیم.

برای آزمایش این تولید کننده‌ی کد، کلاس CarModel را به صورت زیر به پروژه‌ی کنسول آزمایشی اضافه می‌کنیم:
using System.ComponentModel;

namespace NotifyPropertyChangedGenerator.Demo
{
  public partial class CarModel : INotifyPropertyChanged
  {
    private double SpeedKmPerHourBackingField;
    private int NumberOfDoorsBackingField;
    private string ModelBackingField = "";

    public void SpeedUp() => SpeedKmPerHour *= 1.1;
  }
}
این کلاس پیاده سازی کننده‌ی INotifyPropertyChanged است؛ اما به همراه هیچ خاصیت عمومی نیست. فقط به همراه یکسری فیلد خصوصی ختم شده‌ی به «BackingField» است که توسط تولید کننده‌ی کد شناسایی شده و اطلاعات آن‌ها تکمیل می‌شود. فقط باید دقت داشت که این کلاس حتما باید به صورت partial تعریف شود تا امکان تکمیل خودکار کدهای آن وجود داشته باشد.

یک نکته:   در این حالت هرچند برنامه بدون مشکل کامپایل و اجرا می‌شود، ممکن است خطوط قرمزی را در IDE خود مشاهده کنید که عنوان می‌کند این قطعه از کد قابل کامپایل نیست. اگر با چنین صحنه‌ای مواجه شدید، یکبار solution را بسته و مجددا باز کنید تا تولید کننده‌ی کد، به خوبی شناسایی شود. البته نگارش‌های جدیدتر Visual Studio و Rider به همراه قابلیت auto reload پروژه برای کار با تولید کننده‌‌های کد هستند و دیگر شاهد چنین صحنه‌هایی نیستیم و حتی اگر برای مثال فیلد جدیدی را به CarModel اضافه کنیم، نه فقط بلافاصله کدهای متناظر آن تولید می‌شوند، بلکه خواص عمومی تولید شده در Intellisense نیز قابل دسترسی هستند.


نحوه‌ی مشاهده‌ی کدهای خودکار تولید شده

اگر علاقمند باشید تا کدهای خودکار تولید شده را مشاهده کنید، در Visual Studio، در قسمت و درخت نمایشی dependencies پروژه، گره‌ای به نام Analyzers وجود دارد که در آن برای مثال نام NotifyPropertyChangedGenerator و ذیل آن، کلاس‌های تولید شده‌ی توسط آن، قابل مشاهده و دسترسی هستند و حتی قابل دیباگ نیز می‌باشند؛ یعنی می‌توان بر روی سطور مختلف آن، break-point قرار داد.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: SourceGeneratorTests.zip

معرفی تعدادی منبع تکمیلی
- برنامه Source generator playground
در اینجا تعدادی مثال را که توسط مایکروسافت توسعه یافته‌است، مشاهده می‌کنید که اتفاقا یکی از آن‌ها پیاده سازی تولید کننده‌ی کد اینترفیس INotifyPropertyChanged است. در این برنامه، خروجی کدهای تولیدی نیز به سادگی قابل مشاهده‌است.

- برنامه SharpLab
برای توسعه‌ی تولید کننده‌های کد، عموما نیاز است تا با Roslyn API آشنا بود. در این برنامه اگر از منوی بالای صفحه قسمت results، گزینه‌ی «syntax tree» را انتخاب کنید و سپس قسمتی از کد خود را انتخاب کنید، بلافاصله معادل Roslyn API آن، در سمت راست صفحه نمایش داده می‌شود.

- معرفی مجموعه‌ای از Source Generators
در اینجا می‌توان مجموعه‌ای از پروژه‌های سورس باز Source Generators را مشاهده و کدهای آن‌ها را مطالعه کنید و یا از آن‌ها در پروژه‌های خود استفاده نمائید.

- معرفی یک cookbook در مورد Source Generators
این cookbook توسط خود مایکروسافت تهیه شده‌است و جهت شروع به کار با این فناوری، بسیار مفید است.

- مجموعه مثال‌های Source generators از مایکروسافت
در اینجا می‌توانید مجموعه مثال‌هایی از Source generators را که توسط مایکروسافت تهیه شده‌است، مشاهده کنید. شرح و توضیحات تعدادی از آن‌ها را هم در اینجا مطالعه کنید.
مسیرراه‌ها
WPF
          اشتراک‌ها
          ساخت یک محیط توسعه زیبا در اندروید استودیو
           این مطلب برنامه نویسان اندروید را از تنظیمات متعدد و سفارشی سازی محیط کاری  دور خواهدکرد و در واقع با انجام تنظیمات ذیل به محیط کاری که در عکس مشاهده میکنند خواهند رسید .
          • ترکیب رنگی زیبا و طراحی متریال محیط در عکس زیر قابل مشاهده می‌باشد 


          ۱. تنظیمات GUI Theme :
          از منوی File بر روی Settings کلیک می‌کنیم و بر روی Plugin کلیک کرده در قسمت جستجو عبارت مقابل ( Material Theme UI ) را  جستجو کرده و بر روی Install jetBrains Plugin کلیک می‌کنیم که Plugin مورد نظر ما نصب خواهد شد که بعد نصب اندروید استودیو را Restart می‌کنیم . (تنظیمات طبق عکس زیر انجام گردد) 
          دسترسی به مخزن گیت هاب ‍   Material Theme UI for jetBrains  
          ⌘⇧a → "Plugins" → ↩ → ⌃⌥b → <search> → "Material Theme UI" → [Install plugin] → ⌃⌥c → ⎋ → <restart>

          ۲. تنظیمات Editor Scheme :
          از منوی File بر روی Settings کلیک کرده و از قسمت Editor بر روی Color Scheme کلیک کرده و از قسمت Scheme که chroMATERIAL را انتخاب کرده و بر روی Apply/OK  کلیک می‌کنیم.
          ⌘⇧a → "Color Scheme" → [3. ChroMATERIAL]
          و از قسمت Color Scheme Font که Use color scheme font instead را انتخاب و فونت را Lucida Snas Typewriter انتخاب و Apply/ok می‌کنیم.

          ۳. تنظیمات HOLO Logcat :
          به مسیر زیر مراجعه کرده Preferences → Editor → Colors & Fonts → Android Logcat و تنظیمات Logcat را طبق سلیقه خود مشخص می‌کنیم و در نهایت Apply/ok می‌کنیم .
          ⌘⇧a → "Android Logcat" → [Save as...] → "ChroMATERIAL + HOLO"¹ → ↩ → <set foreground colors as in the table ↑> → ⎋
          Type   : Color
          verbose: #BBB debug: #33B5E5 info: #9C0 assert: #A6C error: #F44 warning: #FB3 

          ساخت یک محیط توسعه زیبا در اندروید استودیو
          مطالب
          زیرنویس فارسی ویدئوهای مقدمات AngularJS - قسمت پنجم
          زیرنویس‌های فارسی قسمت پنجم را از اینجا می‌توانید دانلود کنید.
          لیست سرفصل‌های قسمت پنجم به شرح زیر است:
          01-Introduction to Directives
          02-Demo. Creating Your First Directive
          03-Demo. Domain Specific Language via Custom Elements
          04-Demo. Isolating Directive Scope
          05-Demo. Exploring Isolate Scope Bindings
          06-Demo. Handling Events with Directives
          07-Demo. Observing and Responding to Changes
          08-Demo. Using Controllers within Directives
          09-Demo. Sharing Directive Controllers via Require
          10-Demo. Directive Priority and using Terminal
          11-Demo. Using Require with Nested Directives
          12-Demo. Understanding Transclusion
          13-Demo. Using Compile to Transform the DOM
          14-Demo. Making jQuery More Explicit with Directives
          15-Summary
          در این قسمت به مبحث ایجاد دایرکتیوهای سفارشی پرداخته می‌شود. دایرکتیوها در واقع مهمترین قسمت هر برنامه انگولار هستند. انگولار به صورت توکار شامل تعداد زیادی دایرکتیو می‌باشد. در واقع می‌توانیم بگوئیم دایرکتیو می‌تواند یک سینتکس جدید باشد که دارای یک رفتار مخصوص می‌باشد. برای مثال، static HTML هیچ دیدی نسبت به ایجاد و نمایش یک ویجت انتخابگر تاریخ(Date Picker) ندارد. برای اینکار باید به HTML، این سینتکس جدید را توسط دایرکتیوها آموزش دهیم.
          نظرات مطالب
          Angular CLI - قسمت پنجم - ساخت و توزیع برنامه
          «... در این سیستم از AngularCLI استفاده نشده است ...»
          مشکل همینجا است! اگر از Angular CLI استفاده کنید، در پشت صحنه تمام مباحث bundling & minification و همچنین حذف کدهای مرده (استفاده نشده) را به صورت خودکار توسط Webpack، پلاگین‌های آن و کامپایلر مورد استفاده، انجام می‌دهد (و شما نیازی به تنظیم اضافه‌تر و یا دستی برای این مورد ندارید). در کل روش استاندارد کار با Angular با راه اندازی یک پروژه‌ی Angular CLI شروع می‌شود. بنابراین بهتر است آرام آرام کارتان را به این سیستم منتقل کنید (روش کار با System JS که روزهای اول ارائه‌ی Angular مطرح شده بود (همان تصویری که ارسال کردید)، الان دیگر مطلقا استفاده نمی‌شود). همین سری Angular CLI را از قسمت اول آن پیگیری کنید، برای شروع کفایت می‌کند.
          اگر به قسمت «یک مثال: ساخت برنامه‌ی مثال قسمت چهارم - تنظیمات مسیریابی در حالت prod » مطلب جاری دقت کنید، یک تصویر خروجی ذیل آن ارسال شده‌است.

          در این خروجی، کمتر از 5 فایل js قابل مشاهده هستند که حاصل bundling & minification نهایی و تمام فایل‌های برنامه، توسط Angular CLI هستند.
          به علاوه Kendo UI یک نگارش مخصوص Angular را دارد که برای آن از صفر بازنویسی شده‌است و کاملا با Angular CLI سازگار است. همچنین مجموعه کامپوننت‌های سبک‌تر و بهتر دیگری هم برای Angular وجود دارند.
          نظرات مطالب
          آشنایی با TransactionScope
          برای قسمت اول مطالب شما باید عنوان کنم که برای Nested Transaction محدودیت خاصی تعیین نشده است. تا زمانی که Timeout رخ ندهد و همه تراکنش‌ها به درستی Commit شوند مشکلی پیش نخواهد آمد. ولی باید به این نکته دقت داشت که اگر هنگام استفاده از تراکنش‌ها تو درتو یکی از تراکنش‌ها به هر دلیلی موفقیت آمیز نباشد یا زمان آن سپری شود یا زمان انجام کل تراکنش به اتمام برسد Exceptionتولید خواهد شد و تمام عملیات RollBack می‌شوند.البته می‌توان برای مدیریت این تراکنش‌ها از TransactionScopeOption که در مطلب عنوان کردم استفاده کنید. اما در توضیح قسمت دوم مطالب شما باید عنوان کنم که برای اجرای تراکنش به این روش نیاز به مجوز اجرای تراکنش از راه دور است. این کار هم از طریق تنظیمات Remote Access MSDTC میسر می‌شود. برای انجام این کار از قسمت زیر استفاده کنید.
          ComponentServieces/Computers/MyComputer/Distributed Transaction Coodrinator/Local DTC
          بر روی Local DTC کلیک راست کنید و گزینه‌ی Properties رو بزنید. از برگه Security تیک قسمت‌های زیر رو بزنید.
          1-Notwork DTC Access
          2- Allow Remote Client
          3- Allow Remote Administration
          4-Allow Inbound
          5-Allow OutBound
          6 -No Authentication Required.
          خود من به شخصه از این روش برای اجرای سرویس‌های WCF به صورت تراکنش استفاده می‌کنم.