نظرات مطالب
EF Code First #5
- بله. مراجعه کنید به قسمت چهارم مباحث «فعال سازی گزینه‌های مهاجرت خودکار، آزمودن ویژگی مهاجرت خودکار و عکس العمل ویژگی مهاجرت خودکار در مقابل از دست رفتن اطلاعات» آن.
- امکان ردیابی تغییرات آن (منظور از ردیابی در اینجا، ثبت وقایع و ذخیره سازی خروجی SQL به روز رسانی ساختار بانک اطلاعاتی است) هم در همان قسمت چهارم ذیل مبحث «ساده سازی پروسه مهاجرت خودکار» مطرح شده.
نظرات مطالب
بررسی روش مشاهده خروجی SQL حاصل از کوئری‌های Entity framework Core
روش ارتقاء به EF Core 2.2
    public class BloggingContext : DbContext
    {
        public BloggingContext()
        { }

        public BloggingContext(DbContextOptions options)
            : base(options)
        { }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.EnableSensitiveDataLogging();
                optionsBuilder.UseSqlServer(@"...");
                optionsBuilder.ConfigureWarnings(warnings =>
                {
                    warnings.Log(CoreEventId.IncludeIgnoredWarning);
                    warnings.Log(RelationalEventId.QueryClientEvaluationWarning);
                });
                optionsBuilder.UseLoggerFactory(GetLoggerFactory());
            }
        }

        private ILoggerFactory GetLoggerFactory()
        {
            IServiceCollection serviceCollection = new ServiceCollection();
            serviceCollection.AddLogging(builder =>
                   builder.AddConsole()
                          //.AddFilter(category: DbLoggerCategory.Database.Command.Name, level: LogLevel.Information));
                          .AddFilter(level => true)); // log everything
            return serviceCollection.BuildServiceProvider().GetRequiredService<ILoggerFactory>();
        }
    }
در اینجا برای لاگ کردن خروجی EF می‌توان یک ILoggerFactory را که توسط متد AddFilter، سطوح لاگ کردن آن مشخص می‌شود، به UseLoggerFactory ارسال کرد.
مطالب
آشنایی با معماری فیزیکی یک بانک اطلاعاتی
یکی از مهمترین مسائلی که به مدیر پایگاه داده، در پیاده سازی صحیح و نگهداری و برطرف سازی مشکلات می‌تواند کمک کند، شناخت و درک مفاهیم صحیحی از معماری فیزیکی یک بانک اطلاعاتی است. در این مقاله قصد دارم به معرفی برخی از این موارد بپردازم.

1:data pages اساسی‌ترین واحد نگهداری داده در اس کیوال سرور، صفحه نام دارد. فضای دیسک اختصاص یافته به فایل داده بانک، برای یک بانک اطلاعاتی به صورت منطقی به صفحات پیوسته از صفر تا n تقسیم بندی می‌شود. همچنین لازم به ذکر است عملیات خواندن و یا نوشتن در دیسک، در سطح این صفحه‌ها صورت می‌گیرد که در تصویر زیر قابل مشاهده است:


 لازم به ذکر است در sql server هر صفحه، 8 کیلوبایت است. این مورد به این معنی است که هر بانک اطلاعاتی، دارای 128 صفحه به ازای هر یک مگابایت است. هر صفحه دارای 96 بایت با عنوان header یا سرصفحه است که شامل اطلاعات سیستمی در مورد صفحه است. این اطلاعات سیستمی شامل مواردی چون page number یا شماره صفحه و نوع صفحه یا page type و مقدار فضای خالی آن صفحه و شماره شناسایی یک واحد اختصاص یافته یا به اختصار allocation unit id و.... هستند می‌باشد. نکته جالب و قابل توجه این است که فایل‌های ثبت وقایع یا Log files از صفحه استفاده نمی‌کنند؛ بلکه شامل یکسری رکورد log هستند.
 برای بدست آوردن اطلاعات در مورد فایلهای دیتابیس می‌توانید از کد زیر استفاده نمایید SELECT * FROM sys.database_files که خروجی زیر را به شما نشان می‌دهد:


 extents: به ابتدایی‌ترین قسمتی که sql server امکان مدیریت بر آن را دارد extent گویند. هر extent شامل 8 صفحه‌ی به هم پیوسته است. لازم به ذکر است که sql server هر 1 مگابایت را به شانزده extent اختصاص می‌دهد. sql server شامل دونوع extent است که عبارتند از : uniform,mixed uniform extent متعلق به یک شیء است و هر هشت صفحه‌ی آن فقط توسط یک شیء قابل استفاده‌است. mixed extent می‌تواند حداکثر بین هشت شیء به اشتراک گذاشته شود؛ به نحوی که هر یک از هشت صفحه می‌توانند متعلق به یک شیء باشند. همانطور که در شکل زیر می‌بینید به طور پیش فرض با ایجاد یک جدول، یک mixed extent به آن اختصاص داده می‌شود. در صورتیکه این شیء به اندازه‌ی هشت صفحه رشد کند، به آن یک uniform extent اختصاص داده میشود.


فایلهای بانک اطلاعاتی
هر بانک اطلاعاتی در sql server دارای سه نوع فایل است
فایلهای داده اولیه یا به اختصار primary data files
فایلهای داده‌های ثانویه یا به اختصار secondary data files
فایلهای ثبت وقایع یا به اختصار log file
فایل ثبت وقایع برای نگهداری و ثبت وقایع که برای عملیات recovery مورد نیاز است. معمولا یک بانک اطلاعاتی یک log file دارد؛ ولی می‌تواند بیشتر هم داشته باشد. پسوند این نوع فایلها ldf است .

مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت هشتم - دریافت اطلاعات از سرور
اغلب برنامه‌های AngularJS 2.0، اطلاعات خود را از طریق پروتکل HTTP، از سرور دریافت می‌کنند. برنامه یک درخواست Get را صادر کرده و سپس سرور پاسخ مناسبی را ارائه می‌دهد.


مقدمه‌ای بر RxJS

اگر به پیشنیازهای نصب AngularJS 2.0 در قسمت اول این سری دقت کرده باشید، یکی از موارد آن، RxJS است:
"dependencies": {
    "rxjs": "5.0.0-beta.2"
 },
یک Observable، آرایه‌ای است که اعضای آن به صورت غیر همزمان (asynchronously) در طول زمان دریافت می‌شوند. برای مثال پس از شروع یک عملیات async، ابتدا عنصر اول آرایه دریافت می‌شود، پس از مدتی عنصر دوم و در آخر عنصر سوم آن. به همین جهت از Observable‌ها برای مدیریت داده‌های async مانند دریافت اطلاعات از یک وب سرور، استفاده می‌شود.
قرار است Observableها به ES 2016 یا نگارش پس از ES 6 اضافه شوند و یکی از پیشنهادات آن هستند. اما هم اکنون AngularJS 2.0 از این امکان، توسط یک کتابخانه‌ی ثالث، به نام reactive extensions یا Rx، استفاده می‌کند. از RxJS در سرویس HTTP و همچنین مدیریت سیستم رخدادهای AngularJS 2.0 استفاده می‌شود. Observableها امکانی را فراهم می‌کنند تا به ازای دریافت هر اطلاعات async از سرور، بتوان توسط رخداد‌هایی از وقوع آن‌ها مطلع شد.

در نگارش قبلی AngularJS از Promises برای مدیریت اعمال غیرهمزمان استفاده می‌شد. Observableها تفاوت‌های قابل ملاحظه‌ای با Promises دارند:
- یک Promise تنها یک مقدار، یا خطا را بر می‌گرداند؛ اما یک Observable چندین مقدار را در طول یک بازه‌ی زمانی باز می‌گرداند.
- برخلاف Promises، می‌توان عملیات یک Observable را لغو کرد.
- Observableها از عملگرهایی مانند map، reduce، filter و غیره نیز پشتیبانی می‌کنند.

البته باید عنوان کرد که هنوز هم می‌توان از Promises در صورت تمایل در AngularJS 2.0 نیز استفاده کرد.


تنظیمات اولیه‌ی کار با RxJS در AngularJS 2.0

برای استفاده از RxJS در AngularJS 2.0، مراحلی مانند افزودن مدخل اسکریپت http.dev.js، ثبت پروایدر HTTP و importهای لازم، باید طی شوند که در ادامه آن‌ها را بررسی خواهیم کرد:
الف) سرویس HTTP جزئی از angular2/core نیست. به همین جهت مدخل اسکریپت متناظر با آن، باید به صفحه‌ی اصلی سایت اضافه شود که این مورد، در قسمت اول بررسی پیشنیازهای نصب AngularJS 2.0 صورت گرفته‌است:
 <!-- Required for http -->
<script src="~/node_modules/angular2/bundles/http.dev.js"></script>
این تعریف در فایل Views\Shared\_Layout.cshtml (و یا index.html) پروژه‌ی جاری موجود است. همچنین در این صفحه، مدخل Rx.js نیز ذکر شده‌است.

ب) اکنون فایل app.component.ts را گشوده و سرویس HTTP را به آن اضافه می‌کنیم. با نحوه‌ی ثبت سرویس‌ها در قسمت قبل آشنا شدیم:
import { Component } from 'angular2/core';
import { HTTP_PROVIDERS } from 'angular2/http';
import 'rxjs/Rx';   // Load all features
 
import { ProductListComponent } from './products/product-list.component';
import { ProductService } from './products/product.service';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <pm-products></pm-products>
    </div>
    `,
    directives: [ProductListComponent],
    providers: [
        ProductService,
        HTTP_PROVIDERS
    ]
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
از آنجائیکه می‌خواهیم سرویس HTTP، در تمام کامپوننت‌های برنامه در دسترس باشد، آن‌را در بالاترین سطح سلسه مراتب کامپوننت‌های موجود، یا همان کامپوننت ریشه‌ی سایت، ثبت و معرفی می‌کنیم. بنابراین سرویس توکار HTTP یا HTTP_PROVIDERS به لیست پروایدرها، اضافه شده‌است.

ج) پس از آن نیاز است importهای متناظر نیز به ابتدای ماژول فعلی، جهت شناسایی این سرویس و همچنین امکانات rx.js اضافه شوند.
تعریف 'import 'rxjs/Rx به این شکل، به module loader اعلام می‌کند که این کتابخانه را بارگذاری کن، اما چیزی را import نکن. هنگامیکه این کتابخانه بارگذاری می‌شود، کدهای جاوا اسکریپتی آن اجرا شده و سبب می‌شوند که عملگرهای ویژه‌ی Observable آن مانند map و filter نیز در دسترس برنامه قرار گیرند.


ساخت یک سرویس سمت سرور بازگشت لیست محصولات به صورت JSON

چون در ادامه می‌خواهیم لیست محصولات را از سرور دریافت کنیم، برنامه‌ی ASP.NET MVC فعلی را اندکی تغییر می‌دهیم تا این لیست را به صورت JSON بازگشت دهد.
بنابراین ابتدا کلاس مدل محصولات را به نحو ذیل به پوشه‌ی Models اضافه کرده:
namespace MVC5Angular2.Models
{
    public class Product
    {
        public int ProductId { set; get; }
        public string ProductName { set; get; }
        public string ProductCode { set; get; }
        public string ReleaseDate { set; get; }
        public decimal Price { set; get; }
        public string Description { set; get; }
        public double StarRating { set; get; }
        public string ImageUrl { set; get; }
    }
}
و سپس اکشن متد Products، لیست محصولات فرضی این سرویس را بازگشت می‌دهد:
using System.Collections.Generic;
using System.Text;
using System.Web.Mvc;
using MVC5Angular2.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
 
namespace MVC5Angular2.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }
 
        public ActionResult Products()
        {
            var products = new List<Product>
            {
               new Product
               {
                    ProductId= 2,
                    ProductName= "Garden Cart",
                    ProductCode= "GDN-0023",
                    ReleaseDate= "March 18, 2016",
                    Description= "15 gallon capacity rolling garden cart",
                    Price= (decimal) 32.99,
                    StarRating= 4.2,
                    ImageUrl= "app/assets/images/garden_cart.png"
               },
               new Product
               {
                    ProductId= 5,
                    ProductName= "Hammer",
                    ProductCode= "TBX-0048",
                    ReleaseDate= "May 21, 2016",
                    Description= "Curved claw steel hammer",
                    Price= (decimal) 8.9,
                    StarRating= 4.8,
                    ImageUrl= "app/assets/images/rejon_Hammer.png"
               }
            };
 
            return new ContentResult
            {
                Content = JsonConvert.SerializeObject(products, new JsonSerializerSettings
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                }),
                ContentType = "application/json",
                ContentEncoding = Encoding.UTF8
            };
        }
    }
}
در اینجا از JSON.NET جهت بازگشت camel case نام خواص مورد نیاز در سمت کاربر، استفاده شده‌است.
برای مطالعه‌ی بیشتر:
«استفاده از JSON.NET در ASP.NET MVC»
«تنظیمات و نکات کاربردی کتابخانه‌ی JSON.NET»

به این ترتیب، آدرس http://localhost:2222/home/products، خروجی JSON سرویس لیست محصولات را در مثال جاری، ارائه می‌دهد.


ارسال یک درخواست HTTP به سرور توسط AngularJS 2.0

اکنون پس از تنظیمات ثبت و معرفی سرویس HTTP و همچنین برپایی یک سرویس سمت سرور ارائه‌ی لیست محصولات، می‌خواهیم سرویس ProductService را که در قسمت قبل ایجاد کردیم (فایل product.service.ts)، جهت دریافت لیست محصولات از سمت سرور، تغییر دهیم:
import { Injectable } from 'angular2/core';
import { IProduct } from './product';
import { Http, Response } from 'angular2/http';
import { Observable } from 'rxjs/Observable';
 
@Injectable()
export class ProductService {
    private _productUrl = '/home/products';
 
    constructor(private _http: Http) { }
 
    getProducts(): Observable<IProduct[]> {
        return this._http.get(this._productUrl)
                         .map((response: Response) => <IProduct[]>response.json())
                         .do(data => console.log("All: " + JSON.stringify(data)))
                         .catch(this.handleError);
    }
 
    private handleError(error: Response) {
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}
از آنجائیکه این سرویس دارای یک وابستگی تزریق شده‌است، ذکر ()Injectable@، پیش از تعریف نام کلاس، ضروری است.
در سازنده‌ی کلاس ProductService، کار تزریق وابستگی سرویس Http انجام شده‌است. به این ترتیب با استفاده از متغیر خصوصی http_، می‌توان در کلاس جاری به امکانات این سرویس دسترسی یافت (همان «تزریق سرویس‌ها به کامپوننت‌ها» در قسمت قبل).
سپس متد get آن، یک درخواست HTTP از نوع GET را به آدرس مشخص شده‌ی در متغیر productUrl_ ارسال می‌کند (یا همان سرویس سمت سرور برنامه).
سرویس Http و همچنین شیء Response آن در ماژول‌های Http و Response قرار دارند که در ابتدای صفحه import شده‌اند.

متد http get یک Observable را بازگشت می‌دهد که در نهایت خروجی این متد نیز به همان <[]Observable<IProduct، تنظیم شده‌است. Observable یک شیء جنریک است و در اینجا نوع آن، آرایه‌ای از محصولات درنظر گرفته شده‌است.
اکنون که امضای این متد تغییر یافته است (پیش از این صرفا یک آرایه‌ی ساده از محصولات بود)، استفاده کننده (در کلاس ProductListComponent) باید به تغییرات آن از طریق متد subscribe گوش فرا دهد.
فعلا در کلاس جاری، پس از پایان کار دریافت اطلاعات از سرور، اطلاعات نهایی در متد map در دسترس قرار می‌گیرد (که یکی از عملگرهای RxJs است). کار متد map، اصطلاحا projection است. این متد، هر عضو دریافتی از خروجی سرور را به فرمتی جدید نگاشت می‌کند.
هر درخواست HTTP، در اصل یک عملیات async است. یعنی در اینجا توالی که در اختیار Observable ما قرار می‌گیرد، تنها یک المان دارد که همان شیء HTTP Response است.
بنابراین کار متد map فوق، تبدیل شیء خروجی از سرور، به آرایه‌ای از محصولات است.
در اینجا یک سری کدهای مدیریت استثناءها را نیز در صورت بروز مشکلی می‌توان تعریف کرد. برای مثال در اینجا متد catch، کار پردازش خطاهای رخ داده را انجام می‌دهد.
از متد do جهت لاگ کردن عملیات رخ داده و داده‌های دریافتی در کنسول developer tools مرورگرها استفاده شده‌است.

یک نکته:
اگر خروجی JSON از سرور، برای مثال داخل خاصیتی به نام data محصور شده بود، بجای ()response.json می‌بایستی از response.json().data استفاده می‌شد.


گوش فرا دادن به Observable دریافتی از سرور

تا اینجا یک درخواست HTTP GET را به سمت سرور ارسال کردیم و خروجی آن به صورت Observable در اختیار ما است. اکنون نیاز است کدهای ProductListComponent را جهت گوش فرا دادن به این Observable تغییر دهیم. برای این منظور فایل product-list.component.ts را گشوده و تغییرات ذیل را به آن اعمال کنید:
errorMessage: string;
ngOnInit(): void {
    //console.log('In OnInit');
    this._productService.getProducts()
                        .subscribe(
                              products => this.products = products,
                              error => this.errorMessage = <any>error);
}
در کلاس ProductListComponent، در متد ngOnInit که در آن کار آغاز و مقدار دهی وابستگی‌های کامپوننت انجام می‌شود، متد ()productService.getProducts_ فراخوانی شده‌است. این متد یک Observable را بر می‌گرداند. بنابراین برای پردازش نتیجه‌ی آن نیاز است متد subscribe را در ادامه‌ی آن، زنجیر وار ذکر کرد.
اولین پارامتر متد subscribe، کار دریافت نتایج حاصل را به عهده دارد. برای مثال اگر حاصل عملیات در طی سه مرحله صورت گیرد، سه بار نتیجه‌ی دریافتی را می‌توان در اینجا پردازش کرد. البته همانطور که عنوان شد، یک عملیات غیرهمزمان HTTP، تنها در طی یک مرحله، HTTP Response را دریافت می‌کند؛ بنابراین، پارامتر اول متد subscribe نیز تنها یکبار اجرا می‌شود. در اینجا فرصت خواهیم داشت تا آرایه‌ی دریافتی حاصل از متد map قسمت قبل را به خاصیت عمومی products کلاس جاری نسبت دهیم.
پارامتر دوم متد subscribe در صورت شکست عملیات فراخوانی می‌شود. در اینجا حاصل آن به خاصیت جدید errorMessage نسبت داده شده‌است.


اکنون برنامه را مجددا اجرا کنید، هنوز باید لیست محصولات، مانند قبل نمایش داده شود.


یک نکته
اگر برنامه را اجرا کردید و خروجی مشاهده نشد، به کنسول developer tools مرورگر مراجعه کنید؛ احتمالا خطای ذیل در آن درج شده‌است:
 EXCEPTION: No provider for Http!
به این معنا که پروایدر HTTP یا همان HTTP_PROVIDERS، جایی معرفی نشده‌است. البته مشکلی از این لحاظ در برنامه وجود ندارد و این پروایدر در بالاترین سطح ممکن و در فایل app.component.ts ثبت شده‌است. مشکل اینجا است که مرورگر، فایل قدیمی http://localhost:2222/app/app.component.js را کش کرده‌است (به همراه تمام اسکریپت‌های دیگر) و این فایل قدیمی، فاقد تعریف سرویس HTTP است. بنابراین با حذف کش مرورگر و دریافت فایل‌های js جدید، مشکل برطرف خواهد شد.


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


خلاصه‌ی بحث

برای کار با سرور و ارسال درخواست‌های HTTP، ابتدا نیاز است مدخل تعریف http.dev.js به index.html اضافه شود و سپس HTTP_PROVIDERS را در بالاترین سطح کامپوننت‌های تعریف شده، ثبت و معرفی کرد. پس از آن نیاز است RxJs را نیز import کرد. در ادامه، سرویس دریافت لیست محصولات، وابستگی سرویس HTTP را توسط سازنده‌ی خود دریافت کرده و از آن برای صدور یک فرمان HTTP GET استفاده می‌کند. سپس با استفاده از متد map، کار نگاشت شیء Response دریافتی، به فرمت مناسب مدنظر، صورت می‌گیرد.
در ادامه هر کلاسی که نیاز دارد با این کلاس سرویس دریافت اطلاعات کار کند، متد subscribe را فراخوانی کرده و نتیجه‌ی عملیات را پردازش می‌کند.
مطالب
یک سرویس (میکروسرویس) چیست؟ و چگونه آن را مستند کنیم؟ (قسمت اول)
معماری میکروسرویس (یا به اختصار: میکروسرویس) یک سبک معماری نرم افزار می‌باشد که در آن یک نرم افزار، به مجموعه‌ای از سرویس‌ها خرد می‌شود؛ به نحوی که هر سرویس مسئولیت انجام بخشی از منطق کسب و کار را به عهده داشته باشد.
این تقسیم بندی مزایای متعددی را به همراه دارد که نهایتا پیاده سازی و توسعه راحت‌تر نرم افزار‌های بزرگ و پیچیده را ممکن می‌نماید. از جمله مزایای این معماری می‌توان به راحت‌تر شدن مباحث continuous delivery/deployment، مقیاس پذیری بهتر، تحمل خطا، مهاجرت به (و یا استفاده از) تکنولوژی‌های جدید در بخش‌های مختلف نرم افزار و ... اشاره نمود.

مهم‌ترین بخش و تصمیمات شما به عنوان یک معمار نرم افزار، هنگام طراحی با استفاده از این معماری، شناسایی بخش‌های مختلف کسب و کار، جدا سازی و مرزبندی نمودن آنها و نهایتا طراحی سرویس‌ها و تعیین نحوه همکاری آنها با یکدیگر می‌باشد. لذا در هنگام استفاده از معماری میکروسرویس، مرکز توجهات باید کسب و کار باشد و نه مسائل تکنیکال و موضوعاتی مانند Docker, Kubernetes , Serverless و ... . (DDD می‌تواند به شما جهت مرزبندی بخش‌های مختلف کسب و کار و شناسایی سرویس‌ها کمک نماید)

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

مشخصات یک سرویس
هر سرویس در معماری میکروسرویس دارای چندین ویژگی اصلی به شرح زیر می‌باشد:
- Loosely coupled with other services - باید به طور مستقل از سایر سرویس‌ها عمل کند. به این معنا که تغییر و توسعه سایر سرویس‌ها موجب اختلالی در عملکرد این سرویس نگردد و برعکس، تغییر و توسعه این سرویس نباید عملکرد سایر سرویس‌ها را مختل نماید.
- Independently deployable - تیم توسعه دهنده سرویس قادر باشد تا بدون نیاز به هماهنگی با سایر تیم‌ها، خدمات خود (شامل ویژگی‌های جدید و تغییرات) را مستقر (Deploy) نماید.
- Capable of being developed by a small team – سرویس، امکان توسعه توسط یک تیم کوچک را داشته باشد. این مورد به جهت جلوگیری از سربار زیاد ناشی از هماهنگی در تیم‌های بزرگ، ضرورت دارد.
- Highly maintainable and testable – سرویس بسیار قابل نگهداری و قابل آزمایش باشد؛ امکان توسعه، تست و استقرار سریع را داشته باشد.

ساختار یک سرویس
حال که با ویژگی‌ها و مشخصات اصلی یک سرویس آشنا شدیم، در دیاگرام زیر، ساختار درونی یک سرویس را که از معماری هگزاگون (hexagonal architecture) استفاده می‌نماید، بررسی میکنیم. در این معماری، هسته سرویس، منطق کسب کار (Business logic) می‌باشد که توسط چندین آداپتور (جهت ارتباط با سایر سرویس‌ها) احاطه شده است.

بیایید با دقت به هر یک از بخش‌های یک سرویس (با توجه به دیاگرام فوق) نگاه کنیم

هر سرویس  احتمالا دارای یک یا چندین API می‌باشد
از دید مصرف کنندگان یک سرویس (Consumers)، تنها مورد با اهمیت یک سرویس، APIهای آن سرویس می‌باشد. APIهای یک سرویس نیز (با توجه به تصویر فوق) شامل عملیات یا Operations و وقایع منتشر شده یا Published events می‌باشند. که در ادامه این انواع را بررسی میکنیم.

- عملیات (Operations)
به صورت کلی و همانطور که در دیاگرام فوق قابل مشاهده می‌باشد، عملیات به دو نوع دستورات (Commands) و جستارها (Queries) تقسیم می‌شوند. دستورات نوعی از عملیات می‌باشند که موجب تغییر داده‌ها می‌شود؛ اما در مقابل جستارها، عملیاتی در جهت واکشی داده‌ها می‌باشند. برای مثال یک سرویس  ثبت سفارش (OrderService) را در نظر بگیرید. عملیاتی مانند ثبت سفارش ()CreateOrder، انصراف از سفارش ثبت شده  ()CancelOrder و ... عملیاتی از نوع دستورات هستند و عملیاتی مانند یافتن یک سفارش خاص ()FindOrder که هیچ دیتایی را تغییر نمیدهد، از نوع جستارها می‌باشند.
عملیات ارائه شده توسط یک سرویس میتواند از ترکیبی از پروتکل‌های همزمان (Synchronous protocols) مانند REST یا gRPC و پروتکل‌های غیر همزمان (Asynchronous protocols) مانند messaging باشند.
پروتکل‌های همزمان، به ویژه REST، بیشتر در مواردی که قصد ارائه API به کلاینت‌های خارجی (External clients) را داریم، مانند موبایل اپلیکیشن‌ها و یا نرم افزارهای تک صفحه‌ای (SPA) کاربرد دارند.
از پروتکل‌های غیر همزمان مانند messaging نیز بیشتر در مواردی که میخواهیم الگوی ساگا (SAGA) را پیاده سازی نماییم و به روز نگه داشتن داده‌ها را بین سرویس‌های مختلف حفظ کنیم، نیاز به استفاده داریم. برای مثال در همان سیستم ثبت سفارش، عملیات ()CreateOrder به صورت Rest و با متد Post در Endpoint ای مانند /Order پیاده سازی می‌شود و پس از فراخوانی، یک عملیات غیرهمزمان مانند CreateOrderSaga را نیز به صورت messaging آغاز میکند.

- وقایع (Events)
سرویس‌ها، اغلب وقایعی (Event) را نیز منتشر میکنند. منظور از وقایع یا events معمولا همان مفهوم domain event درDDD می‌باشد که در همان ادبیات DDD وقایع توسط aggregate‌ها در زمان هایی مانند ایجاد، ویرایش، حذف و یا سایر مفاهیم موجود در منطق کسب و کار منتشر می‌شوند. سرویس نیز معمولا این وقایع را روی یک کانال ارتباطی (message channel) که توسط یک message broker (مانند RabbitMQ, Apache Kafka, ActiveMQ و ...) پیاده سازی شده است، منتشر میکند. و علاقمندان به دریافت این وقایع می‌توانند وقایع را پس از انتشار، بر روی کانال ارتباطی دریافت نمایند.


منطق کسب و کار (Business Logic)
منطق کسب و کار، قلب هر سرویس و دلیل وجود آن سرویس می‌باشد که API هایی را در قالب عملیات (Opertaions) پیاده سازی و همچنین مواردی را در قالب وقایع (Events) منتشر می‌نماید. همچنین منطق کسب و کار می‌تواند بنا بر نیاز خود، عملیات مربوط به سایر سرویس‌ها را فراخوانی و یا در کانال‌های ارتباطی (channels) مربوط به وقایع آنها، مشترک (Subscribes) شود و نهایتا داده‌ها را در دیتابیس خود نگهداری نماید.

نحوه همکاری سرویس‌ها با یکدیگر (Services Collaborations)
با توجه به مفاهیم فوق، زمانی که صحبت از همکاری (collaborate) بین سرویس‌ها می‌شود، معمولا منظور، ارتباط آنها از طریق APIهای یکدیگر (شامل عملیات و وقایع که پیش‌تر توضیح داده شد) می‌باشد (به جای خواندن و نوشتن مستقیم در دیتابیس‌های مربوط به یکدیگر می‌باشد).
برای مثال یک سرویس ممکن است عملیات مربوط به ایجاد سفارش ()CreateOrder را از سرویس ثبت سفارش (OrderService) فراخوانی نماید و یا برعکس خود سرویس ثبت سفارش (OrderService) ممکن است بر حسب نیاز منطق کسب و کار خود، عملیات ارائه شده توسط سرویس انبار را فراخوانی نماید.
همچنین یک سرویس جهت همکاری با دیگر سرویس‌ها میتواند در وقایع منتشر شده (Published events) توسط آنها مشترک (Subscribes) شود. برای مثال سرویس ثبت سفارش احتمالا در وقایع منتشر شده از سوی سرویس رستوران مشترک می‌شود.

دیتابیس اختصاصی
معمولا هر سرویس دارای یک یا چند دیتابیس می‌باشد که دیتای اختصاصی مربوط به منطق کسب و کار خود و در مواردی بخشی از دیتای مربوط به سایر سرویس‌ها را در آن‌(ها) نگهداری میکند. برای مثال اطلاعات سفارش‌ها را هم سرویس ثبت سفارش و هم سرویس رستوران، هر دو نگهداری میکنند و عملا این دیتا ابتدا در سرویس رستوران و سپس در سرویس ثبت سفارش، مجددا نگهداری می‌شود و به نوعی دیتای فوق Replicate شده و تکراری می‌باشد. اما به جهت اطمینان از کاهش وابستگی (loose coupling) این تکرار داده‌ها انجام می‌شود. در مجموع استفاده از یک دیتابیس مشترک (منظور table مشترک می‌باشد) بین سرویس‌ها ایده‌ی بدی می‌باشد و سرویس‌ها باید از طریق API‌های یکدیگر باهم همکاری نمایند.

نتیجه
در این مقاله عنوان شد که میکروسرویس یک سبک معماری می‌باشد و در این معماری، نرم افزار و منطق کسب و کار، به چندین سرویس مختلف  تقسیم می‌شود. مشخصات کلیدی که هر سرویس باید در این سبک معماری (microservice architecture) داشته باشد و همچنین ساختار درونی هر سرویس بررسی شد.
در قسمت بعدی این مقاله، در مورد نحوه مستند سازی این سرویس‌ها صحبت می‌شود. چرا که با زیاد شدن تعداد سرویس‌ها، در صورت عدم وجود یک مستندات مناسب (documents)، ارتباط و هماهنگی تیم‌ها با یکدیگر خود موجب سربار خواهد شد.

منابع
برگرفته شده از مقاله آقای ریچاردسون (whats-a-service
مطالب
تزریق وابستگی‌ها در ASP.NET Core - بخش 1 - مقدمه
مقدمه

زمانیکه یک برنامه را بر پایه‌ی شیء گرایی طراحی می‌کنید و می‌نویسید، به صورت معمول جریان وابستگی‌ها در برنامه‌ی شما به صورت زیر است:


 
در این حالت برای کامپایل شدن برنامه نیاز است که فرآیند کامپایل از دورترین کلاس و متد شروع شود. همانطور که می‌بینید در اینجا هر کلاس به تمام زیر کلاس‌های خود وابسته است و هر تغییر در هر کدام از کلاس‌های خدمتگزار می‌تواند تاثیرات مستقیمی بر روی سایر کلاس‌ها داشته باشد. در واقع جریان کنترل برنامه‌ی ما بجای اینکه در اختیار کلاس‌های سیاست گذار ( کلاس‌های بالایی در شکل) باشد، به دست کلاس‌های خدمتگزار افتاده است.  این قضیه باعث درهم تنیدگی و جفت شدگی کدها و کلاس‌ها به یکدیگر می‌شود که مشکل‌زاست و امکان نگهداری، تغییرات و توسعه‌ی برنامه را به شدت کاهش می‌دهد.

به عبارت دیگر در طراحی ساخت یافته، کلاس‌های سطح بالا، به کلاس‌های سطح پایین وابسته‌اند. این مسئله دو مشکل را ایجاد می‌کند:
  1. هر تغییری در کلاس‌های سطح پایین ممکن است باعث ایجاد اشکالی در کلاس‌های سطح بالا گردد.
  2. استفاده‌ی مجدد از کلاس‌های سطح بالا در جاهای دیگر مشکل است؛ زیرا وابستگی مستقیمی به کلاس‌های سطح پایین دارند.

اصل معکوس سازی / وارونگی وابستگی‌ها Dependency Inversion Principle
 
یکی از اصول پنجگانه‌ی طراحی برنامه‌های شیء گرا  که با نام اصول SOLID  شناخته می‌شوند، اصل «وارونگی وابستگی‌ها» است که روشی برای مشکل جفت شدگی و وابستگی کلاس‌ها به یکدیگر را به صورت تئوری ارائه می‌دهد.

 اصل وارونگی وابستگی‌ها بیان می‌کند:
  •   ماژول‌های (کلاس‌های) سطح بالا نباید به ماژولهای (کلاس‌های) سطح پایین وابسته باشند و هر دو باید به انتزاعات وابسته باشند (برای مثال interface‌ها).
  •   انتزاعات نباید وابسته به جزئیات باشند؛ بلکه جزئیات (پیاده سازی) باید وابسته به انتزاعات باشند.

بر اساس این اصل، ما باید در سطوح بالا سیاست گذاری‌ها و انتزاعات را در قالب interface‌ها تعریف کرده و کلاس‌های سطح بالای خود را بر همین اساس پیاده سازی کنیم و در سطوح پائین‌تر، پیاده سازی‌هایی را بر اساس انتزاعات و سیاست گذاری‌های سطوح بالاتر، انجام دهیم.

در شکل زیر، حالت عادی جریان کنترل را می‌بینید .
 


همانطور که می‌بینید، کلاس M برای اجرا، وابسته به کلاس N و متد F در درون آن است. در اینجا ما با استفاده از اینترفیس‌ها می‌توانیم جریان کنترل را معکوس یا وارونه کنیم که به این عمل «وارونگی کنترل یا Inversion of Control» می‌گویند.


 
شیء گرایی در واقع در مورد نحوه‌ی جریان کنترل است. در اینجا اینترفیس‌ها به ما کنترل کاملی را بر جریان کنترل (Flow of control) می‌دهند که با استفاده از این امکان می‌توانیم از نوشتن کدهای جفت شده، شکننده و کلاس‌هایی یکبار مصرف، بپرهیزیم.


الگوی Dependency Injection 
تزریق وابستگی یا Dependency Injection، یک الگوی طراحی است که از آن برای طراحی و پیاده سازی IoC Container‌ها استفاده می‌شود. این الگو به ما اجازه می‌دهد که اشیاء وابسته را خارج از کلاس بسازیم و آنها را به طریقی دیگر به کلاس، جهت استفاده ارائه دهیم. به‌وسیله‌ی DI ما ساخت و اتصال اشیاء وابسته به کلاس را از تعریف آن خارج می‌کنیم.

الگوی تزریق وابستگی 3 نوع کلاس را درگیر می‌کند:
  •  کلاس کلاینت / Client Class : کلاس کلاینت (کلاس وابسته) کلاسی است که به کلاس سرویس وابسته است .
  • کلاس سرویس  /  Service Class :  کلاس سرویس (وابستگی) کلاسی است که یک سرویس را به کلاس کلاینت ارائه می‌دهد.
  •  کلاس تزریق کننده / Injector Class  :  کلاس تزریق کننده، نمونه‌ای از کلاس سرویس را ساخته و به کلاس کلاینت، تزریق می‌کند.

شکل زیر وابستگی بین کلاس‌ها را شرح می‌دهد:

 
همانطور که می‌بینید، کلاس Injector، نمونه‌ای از کلاس سرویس را می‌سازد و آن را به نمونه‌ای از کلاس Client تزریق می‌کند. با این کار، DI، وظیفه‌ی ساخت یک نمونه از کلاس Service را از درون کلاس Client جدا می‌کند.

انواع تزریقات وابستگی‌ها:
به صورت کلی به سه روش و در سه مکان، امکان تزریق وابستگی کلاس سرویس، درون کلاس کلاینت وجود دارد:
  •   تزریق درون سازنده / Constructor Injection  : در تزریق درون سازنده، در سازنده‌ی کلاس کلاینت، لیستی از سرویس‌های مورد نیاز کلاس، که کلاس، برای عملکرد خود به آن‌ها «وابسته» است، ثبت می‌شوند و کلاس Injector، سرویس (وابستگی) مورد نظر را درون سازنده‌ی کلاس Client ارائه می‌دهد.
  •   تزریق درون Property  کلاس / Property Injection : در این حالت که همچنین با نام (Setter Injection) هم شناخته می‌شود، تزریق کننده، وابستگی را به وسیله‌ی یک Property عمومی کلاس کلاینت ارائه می‌دهد.
  •  تزریق درون متد / Method Injection  : در این حالت، خود کلاس کلاینت، یک پیاده سازی از یک interface را ارائه می‌کند که درون آن متدهایی برای ارائه‌ی وابستگی‌ها به کلاینت تعریف شده‌اند. در این وضعیت، تزریق کننده از این اینترفیس برای ارائه‌ی وابستگی‌ها به کلاینت درون متدها، استفاده می‌کند. 

هر کدام از روش‌های فوق مزایا و معایب خود را دارند، ولی در NET Core. بیشتر از «تزریق درون سازنده» استفاده می‌شود. در صورت لزوم می‌توانید از اینجا نمونه‌هایی از تزریق وابستگی را به هر کدام از سه روش بالا، مشاهده کنید.

Inversion of Control Container
در واژگان فنی مهندسی نرم افزار، Container (محفظه)  به جزیی از برنامه گفته می‌شود که می‌تواند اجزای دیگر برنامه را در بر بگیرد.  IoC Container ‌ها در واقع فریم ورک‌ها/چارچوب‌ها یا کتابخانه‌های برنامه نویسی هستند که ما در آنها می‌توانیم اشیاء مختلف را به سبک‌های خاصی تعریف و ثبت کنیم و در مواقع لزوم آنها را واکشی و به کلاس‌ها تزریق کنیم. معمولا  IoC Container‌ها لیستی از اشیاء هستند که در آن اینترفیس‌ها و پیاده سازی‌های مربوط به هر کدام از آن‌ها ثبت می‌شوند. درون IoC Container برای پیاده سازی اصل وارونگی وابستگی‌ها، معمولا از یکی از دو الگوی زیر استفاده می‌کنند (گاها هم دو الگو را با هم پیاده سازی می‌کنند) :
  •  Dependency Injection 
  • Service Locator

تمرکز اصلی ما در این نوشتار بر روی DI Container هاست. فرق Dependency Injection و Service Locator در این است که در DI، وابستگی‌ها توسط IoC Container از درون محفظه واکشی می‌شوند و به درون کد تزریق می‌شوند ولی در Service Locator در هر جایی از برنامه می‌توان با استفاده‌ی مستقیم از Container و با استفاده از متدهایی که به ما ارائه می‌دهد،  پرس و جو کرد (کوئری زد) و وابستگی مورد نظر را واکشی کرد.
در تزریق وابستگی، کلاس استفاده کننده از سرویس‌ها، درگیر نحوه‌ی واکشی و نمونه سازی از سرویس مورد نظر خود نمی‌شود و همه‌ی کار توسط DI Container انجام می‌گیرد. ولی در Service Locator باید سرویس مورد نظر، درون خود کلاس، مستقیما از Container دریافت و ساخته شود.
برای استفاده از Service Locator، تنها پیش نیاز، دسترسی به شیء Service Locator است.

به صورت کلی IoC Container ‌ها سه وظیفه‌ی اساسی را برعهده دارند:
  • ثبت سرویس درون خود
  •   ساخت نمونه‌های مورد نظر از سرویس‌ها و ارائه دادن آن‌ها به کلاس‌هایی که نیاز دارند.
  •  از بین بردن نمونه سرویس‌های ساخته شده (Dispose) کردن آن‌ها .

در ادامه با ساخت پروژه‌ای، اولین سرویس خودمان را درون Microsoft Dependency Injection Container یا به اختصار DI Container، ثبت کرده و آن را واکشی می‌کنیم.
مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت یازدهم - پیاده سازی پویای Decoratorها توسط Castle.Core
در قسمت قبل، نحوه‌ی پیاده سازی الگوی Decorator را با استفاده از امکانات تزریق وابستگی‌های NET Core. بررسی کردیم؛ اما ... این روزها کسی Decoratorها را دستی ایجاد نمی‌کند. یعنی اگر قرار باشد به ازای هر کلاسی و هر سرویسی، یکبار کلاس Decorator آن‌را با پیاده سازی همان اینترفیس سرویس اصلی و فراخوانی دستی تک تک متدهای سرویس اصلی تزریق شده‌ی در سازنده‌ی آن انجام دهیم، آنچنان کاربردی به نظر نمی‌رسد. به همین منظور کتابخانه‌هایی تحت عنوان Dynamic Proxy تهیه شده‌اند تا کار ساخت و پیاده سازی پویای Decorator‌ها را انجام دهند. در این بین ما فقط منطق برای مثال مدیریت استثناءها، لاگ کردن ورودی‌ها و خروجی‌های متدها، کش کردن خروجی متدها، سعی مجدد اجرای متدهای با شکست مواجه شده و ... تک تک متدهای یک سرویس را به آن‌ها معرفی می‌کنیم و سپس پروکسی‌های پویا، کار محصور سازی خودکار اشیاء ساخته شده‌ی از سرویس اصلی را برای ما انجام می‌دهند. این روش نه فقط کار نوشتن دستی Decorator کلاس‌ها را حذف می‌کند، بلکه عمومی‌تر نیز بوده و به تمام سرویس‌ها قابل اعمال است.


Interceptors پایه‌ی پروکسی‌های پویا هستند

برای پیاده سازی پروکسی‌های پویا نیاز است با مفهوم Interceptors آشنا شویم. به کمک Interceptors فرآیند فراخوانی متدها و خواص یک کلاس، تحت کنترل و نظارت قرار خواهند گرفت. زمانیکه یک IOC Container کار وهله سازی کلاس سرویس خاصی را انجام می‌دهد، در همین حین می‌توان مراحل شروع، پایان و خطاهای متدها یا فراخوانی‌های خواص را نیز تحت نظر قرار داد و به این ترتیب مصرف کننده، امکان تزریق کدهایی را در این مکان‌ها خواهد یافت. مزیت مهم استفاده از Interceptors، عدم نیاز به کامپایل ثانویه اسمبلی‌های موجود، برای تغییری در کدهای آن‌ها است (برای تزریق نواحی تحت کنترل قرار دادن اعمال) و تمام کارها به صورت خودکار در زمان اجرای برنامه مدیریت می‌گردند.

با اضافه کردن Interception به پروسه وهله سازی سرویس‌ها توسط یک IoC Container، مراحل کار اینبار به صورت زیر تغییر می‌کنند:
الف) در اینجا نیز در ابتدا فراخوان، درخواست وهله‌ای را بر اساس اینترفیسی خاص، به IOC Container ارائه می‌دهد.
ب) IOC Container نیز سعی در وهله سازی درخواست رسیده را بر اساس تنظیمات اولیه‌ی خود می‌کند.
ج) اما در این حالت IOC Container تشخیص می‌دهد نوعی که باید بازگشت دهد، علاوه بر وهله سازی، نیاز به مزین سازی و پیاده سازی Interceptors را نیز دارد. بنابراین نوع مورد انتظار را در صورت وجود، به یک Dynamic Proxy، بجای بازگشت مستقیم به فراخوان ارائه می‌دهد.
د) در  ادامه Dynamic Proxy، نوع مورد انتظار را توسط Interceptors محصور کرده و به فراخوان بازگشت می‌دهد.
ه) اکنون فراخوان، در حین استفاده از امکانات شیء وهله سازی شده، به صورت خودکار مراحل مختلف اجرای یک Decorator را سبب خواهد شد.

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


پیاده سازی پروکسی‌های پویا توسط کتابخانه‌ی Castle.Core در برنامه‌های NET Core.

در ادامه از کتابخانه‌ی بسیار معروف Castle.Core برای پیاده سازی پروکسی‌های پویا استفاده خواهیم کرد. از این کتابخانه در پروژه‌ی EF Core، برای پیاده سازی Lazy loading نیز استفاده شده‌است.
برای دریافت آن یکی از دستورات زیر را اجرا نمائید:
> Install-Package Castle.Core
> dotnet add package Castle.Core
و یا به صورت خلاصه برای افزودن آن، فایل csproj برنامه به صورت زیر تغییر می‌کند:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <ItemGroup>
    <PackageReference Include="castle.core" Version="4.3.1" />
  </ItemGroup>
</Project>


تبدیل ExceptionHandlingDecorator مثال قسمت قبل، به یک Interceptor مخصوص Castle.Core

در قسمت قبل، کلاس MyTaskServiceDecorator را جهت اعمال یک try/catch به همراه logging، به متد Run سرویس MyTaskService، تهیه کردیم. در اینجا قصد داریم نگارش عمومی‌تر این تزئین کننده را با طراحی یک Interceptor مخصوص Castle.Core انجام دهیم:
using System;
using Castle.DynamicProxy;
using Microsoft.Extensions.Logging;

namespace CoreIocSample02.Utils
{
    public class ExceptionHandlingInterceptor : IInterceptor
    {
        private readonly ILogger<ExceptionHandlingInterceptor> _logger;

        public ExceptionHandlingInterceptor(ILogger<ExceptionHandlingInterceptor> logger)
        {
            _logger = logger;
        }

        public void Intercept(IInvocation invocation)
        {
            try
            {
                invocation.Proceed(); //فراخوانی متد اصلی در اینجا صورت می‌گیرد
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "An unhandled exception has been occurred.");
            }
        }
    }
}
برای تهیه‌ی یک کلاس Interceptor، کار با پیاده سازی اینترفیس IInterceptor شروع می‌شود. در اینجا فراخوانی متد ()invocation.Proceed، دقیقا معادل فراخوانی متد اصلی سرویس است؛ شبیه به کاری که در قسمت قبل انجام دادیم:
        public void Run()
        {
            try
            {
                _decorated.Run();
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "An unhandled exception has been occurred.");
            }
        }
فراخوان، یک نمونه‌ی تزئین شده‌ی از سرویس درخواستی را دریافت می‌کند. زمانیکه متد Run این نمونه‌ی ویژه را اجرا می‌کند، در حقیقت وارد متد Run این Decorator شده‌است که اینبار در پشت صحنه، توسط Dynamic proxy ما به صورت پویا ایجاد می‌شود. اکنون جائیکه ()invocation.Proceed فراخوانی می‌شود، دقیقا معادل همان ()decorated.Run_ قسمت قبل است؛ یا همان اجرای متد اصلی سرویس مدنظر. اینجا است که می‌توان منطق‌های سفارشی را مانند لاگ کردن، کش کردن، سعی مجدد در اجرا و بسیاری از حالات دیگر، پیاده سازی کرد.


اتصال ExceptionHandlingInterceptor تهیه شده به سیستم تزریق وابستگی‌ها

در ادامه روش معرفی ExceptionHandlingInterceptor تهیه شده را به سیستم تزریق وابستگی‌ها، توسط متد Decorate کتابخانه‌ی Scrutor که آن‌را در قسمت قبل بررسی کردیم، ملاحظه می‌کنید:
namespace CoreIocSample02
{
    public class Startup
    {
        private static readonly ProxyGenerator _dynamicProxy = new ProxyGenerator();

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<ITaskService, MyTaskService>();
            services.AddTransient<ExceptionHandlingInterceptor>();
            services.Decorate(typeof(ITaskService),
             (target, serviceProvider) =>
                _dynamicProxy.CreateInterfaceProxyWithTargetInterface(
                  interfaceToProxy: typeof(ITaskService),
                  target: target,
                  interceptors: serviceProvider.GetRequiredService<ExceptionHandlingInterceptor>())
            );
- ProxyGenerator به همین نحو static readonly باید تعریف شود و در کل برنامه یک وهله از آن مورد نیاز است.
- سپس نیاز است خود سرویس اصلی غیر تزئین شده، به نحو متداولی به سیستم معرفی شود.
- در ادامه توسط متد الحاقی Decorate، کار تزئین ITaskService را با یک dynamicProxy که ExceptionHandlingInterceptor را به صورت پویا تبدیل به یک Decorator کرده و بر روی تک تک متدهای سرویس ITaskService اجرا می‌کند، انجام می‌دهیم.
- کاری که Scrutor در اینجا انجام می‌دهد، یافتن سرویس ITaskService معرفی شده‌ی پیشین و تعویض آن با dynamicProxy می‌باشد. بنابراین نیاز است تعریف services.AddTransient، پیش از تعریف services.Decorate انجام شده باشد.

یک نکته: چون ExceptionHandlingInterceptor دارای پارامتر تزریق شده‌ای در سازنده‌ی آن است، بهتر است خود آن‌را نیز به صورت یک سرویس ثبت کنیم و سپس وهله‌ای از آن‌را از طریق serviceProvider.GetRequiredService در قسمت interceptors متد CreateInterfaceProxyWithTargetInterface معرفی کنیم تا نیازی به مقدار دهی دستی تک تک پارامترهای سازنده‌ی آن نباشد.

اکنون اگر برنامه را اجرا کنیم و برای مثال ITaskService را در سازنده‌ی یک کنترلر تزریق کنیم، بجای دریافت وهله‌ای از کلاس MyTaskService، اینبار وهله‌ای از Castle.Proxies.ITaskServiceProxy را دریافت می‌کنیم.


به این معنا که Castle.Core به صورت پویا وهله‌ی سرویس MyTaskService را داخل یک Castle.Proxies پیچیده‌است و از این پس ما از طریق این واسط، با سرویس اصلی MyTaskService ارتباط برقرار خواهیم کرد. برای درک بهتر این مراحل، بر روی سازنده‌ی کلاس کنترلر و همچنین متد Intercept، تعدادی break-point را قرار دهید.
مطالب
مهارت‌های تزریق وابستگی‌ها در برنامه‌های NET Core. - قسمت چهارم - پرهیز از الگوی Service Locator در برنامه‌های وب
الگوی Service locator را در قسمت دوم بررسی کردیم. همانطور که عنوان شد، بهتر است تا جائیکه امکان دارد از بکارگیری آن به علت ضدالگو بودن پرهیز کرد. در ادامه قسمت‌های مختلف یک برنامه‌ی ASP.NET Core را که می‌توان بدون نیاز به استفاده‌ی الگوی Service locator، تزریق وابستگی‌ها را در آن‌ها انجام داد، مرور می‌کنیم.


در کلاس آغازین برنامه

در اینجا در متد Configure آن تنها کافی است اینترفیس سرویس مدنظر خود را مانند IAmACustomService، به صورت یک پارامتر جدید اضافه کنید. کار وهله سازی آن توسط Service Provider برنامه به صورت خودکار صورت می‌گیرد:
public class Startup 
{ 
    public void ConfigureServices(IServiceCollection services) { } 
  
    public void Configure(IApplicationBuilder app, IAmACustomService customService) 
    { 
        // ....    
    }         
}

یک نکته‌ی مهم: اگر طول عمر IAmACustomService را Scoped تعریف کرده‌اید و این سرویس از نوع IDisposable نیز می‌باشد، این روش کارآیی نداشته و باید از نکته‌ی «روش صحیح Dispose اشیایی با طول عمر Scoped، در خارج از طول عمر یک درخواست ASP.NET Core» که در قسمت قبل معرفی شد استفاده کنید.


در میان افزارها

هم سازنده‌ی یک میان افزار و هم متد Invoke آن قابلیت تزریق وابستگی‌ها را دارند:
public class TestMiddleware 
{ 
    public TestMiddleware(RequestDelegate next, IAmACustomService service) 
    { 
        // ... 
    } 
 
    public async Task Invoke(HttpContext context, IAmACustomService service) 
    { 
        // ... 
    }     
}
از سازنده‌ی آن برای تزریق وابستگی سرویس‌هایی با طول عمر Singleton استفاده کنید. ServiceProvider به همراه ویژگی است به نام Scope Validation. در این حالت اگر طول عمر سرویسی Singleton باشد (مانند طول عمر یک میان‌افزار) و در سازنده‌ی آن یک سرویس با طول عمر Scoped تزریق شود، در زمان اجرا یک استثناء را صادر می‌کند؛ چون در این حالت رفتار این سرویس Scoped نیز Singleton می‌شود که احتمالا مدنظر شما نیست. در این حالت از پارامترهای اضافی متد Invoke می‌توان برای تزریق وابستگی‌هایی با طول عمر Transient و یا Scoped استفاده کرد.
البته می‌توان این Scope Validation را در فایل program.cs به نحو زیر غیرفعال کرد، ولی بهتر است اینکار را انجام ندهید و همان مقدار پیش‌فرض آن بسیار مناسب است:
public static IWebHostBuilder CreateDefaultBuilder(string[] args) 
{ 
            var builder = new WebHostBuilder() 
//...
                .UseDefaultServiceProvider((context, options) => 
                { 
                    options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); 
                }) 
//...

در کنترلرها

سازنده‌های کنترلرهای برنامه‌های ASP.NET Core قابلیت تزریق وابستگی‌ها را دارند:
public class HelloController : Controller 
{ 
    private readonly IAmACustomService _customService; 
 
    public HelloController(IAmACustomService customService) 
    { 
        _customService = customService; 
    } 
 
    public IActionResult Get() 
    { 
        // ... 
    } 
}
در اینجا حتی می‌توان با استفاده از ویژگی FromServices، یک سرویس را توسط پارامترهای یک اکشن متد نیز درخواست کرد:
[HttpGet("[action]")]
public IActionResult Index([FromServices] IAmACustomService service)
{  
   // ...
}
در این حالت بجای model binding، کار دریافت این سرویس درخواستی صورت می‌گیرد.


در مدل‌ها

ویژگی FromServices بر روی مدل‌ها نیز کار می‌کند.
public IActionResult Index(TestModel model)
{
  // ...
}
در اینجا نحوه‌ی تعریف TestModel را به همراه ویژگی FromServices مشاهده می‌کنید:
public class TestModel 
{        
    public string Name { get; set; } 
 
    [FromServices] 
    public IAmACustomService CustomService { get; set; } 
}
این حالت که property injection نیز نام دارد، نیاز به خاصیتی با یک public setter را دارد.


در Viewها

در Razor Views نیز می‌توان توسط inject directive@ کار تزریق وابستگی‌ها را انجام داد:
 @inject IAmACustomService CustomService
 

در ویژگی‌ها و فیلترها

در ASP.NET Core تزریق وابستگی‌های در سازنده‌های فیلترها نیز کار می‌کند:
public class ApiExceptionFilter : ExceptionFilterAttribute  
{  
    private ILogger<ApiExceptionFilter> _logger;  
    private IHostingEnvironment _environment;  
    private IConfiguration _configuration;  
  
    public ApiExceptionFilter(IHostingEnvironment environment, IConfiguration configuration, ILogger<ApiExceptionFilter> logger)  
    {  
        _environment = environment;  
         _configuration = configuration;  
         _logger = logger;  
    }
در این حالت چون سازنده‌ی این ویژگی، پارامتر دار شده‌است و این پارامترها نیز یک مقدار ثابت قابل کامپایل نیستند، برای معرفی یک چنین فیلتری باید از ServiceFilterها به صورت زیر استفاده کرد:
[Route("api/[controller]")]  
[ApiController]  
[ServiceFilter(typeof(ApiExceptionFilter))]  
public class ValuesController : ControllerBase  
{
نظرات مطالب
ASP.NET MVC #23
بسته‌های کامل MVC نصب هستند روی سرور؟
ELMAH  را به برنامه اضافه کنید تا جزئیات خطاها را بهتر بتوانید لاگ و مشاهده کنید (مهم).
همچنین این خطاها در قسمت لاگ‌های سرور هم ثبت می‌شوند که بهتر است جزئیات آن‌ها را بررسی کنید.
مطالب
فرم‌های مبتنی بر قالب‌ها در Angular - قسمت پنجم - ارسال اطلاعات به سرور
تا اینجا تنظیمات اصلی فرم ثبت اطلاعات کارمندان را انجام دادیم. اکنون نوبت به ارسال این اطلاعات به سمت سرور است. پیشنیاز آن نیز تدارک مواردی است که در مطلب «یکپارچه سازی Angular CLI و ASP.NET Core در VS 2017» پیشتر بحث شدند. از این مطلب تنها تنظیمات موارد ذیل را نیاز خواهیم داشت و از تکرار آن‌ها در اینجا صرفنظر می‌شود تا هم مطلب کوتاه‌تر شود و هم بتوان بر روی اصل موضوع جاری، تمرکز کرد:
- ایجاد یک پروژه‌ی جدید ASP.NET Core در VS 2017
- تنظیمات یک برنامه‌ی ASP.NET Core خالی برای اجرای یک برنامه‌ی Angular CLI
- تنظیمات فایل آغازین یک برنامه‌ی ASP.NET Core جهت ارائه‌ی برنامه‌های Angular
- ایجاد ساختار اولیه‌ی برنامه‌ی Angular CLI در داخل پروژه‌ی جاری: این مورد را تاکنون انجام داده‌ایم و تکمیل کرده‌ایم. بنابراین تنها کاری که نیاز است انجام شود، cut و paste محتوای پوشه‌ی angular-template-driven-forms-lab (پروژه‌ی این سری) به ریشه‌ی پروژه‌ی ASP.NET Core است.
- تنظیم محل خروجی نهایی Angular CLI به پوشه‌ی wwwroot
- روش اول و یا دوم اجرای برنامه‌های مبتنی بر ASP.NET Core و Angular CLI

البته سورس کامل تمام این تنظیمات را از انتهای بحث نیز می‌توانید دریافت کنید.
ضمن اینکه هیچ نیازی هم به استفاده از VS 2017 نیست و هر دوی برنامه‌ی Angular و ASP.NET Core را می‌توان توسط VSCode به خوبی مدیریت و اجرا کرد.


ایجاد ساختار مقدماتی سرویس ارسال اطلاعات به سرور

در برنامه‌های Angular مرسوم است جهت کاهش مسئولیت‌های یک کلاس و امکان استفاده‌ی مجدد از کدها، منطق ارسال اطلاعات به سرور، به درون کلاس یک سرویس منتقل شود و سپس این سرویس به کلاس‌های کامپوننت‌ها، برای مثال یک فرم ثبت اطلاعات، برای ارسال و یا دریافت اطلاعات، تزریق گردد. به همین جهت، ابتدا ساختار ابتدایی این سرویس و تنظیمات مرتبط با آن‌را انجام می‌دهیم.
ابتدا از طریق خط فرمان به پوشه‌ی ریشه‌ی برنامه وارد شده (جائیکه فایل Startup.cs قرار دارد) و سپس دستور ذیل را اجرا می‌کنیم:
 >ng g s employee/FormPoster -m employee.module
با این خروجی
 installing service
  create src\app\employee\form-poster.service.spec.ts
  create src\app\employee\form-poster.service.ts
  update src\app\employee\employee.module.ts
همانطور که در سطر آخر نیز ملاحظه می‌کنید، فایل employee.module.ts را جهت درج کلاس جدید FormPosterService در قسمت providers ماژول آن به روز رسانی می‌کند؛ تا بتوانیم این سرویس را در کامپوننت‌های این ماژول تزریق کرده و استفاده کنیم.
ساختار ابتدایی این سرویس را نیز به نحو ذیل تغییر می‌دهیم:
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

import { Employee } from './employee';

@Injectable()
export class FormPosterService {

    constructor(private http:Http) {
    }

    postEmployeeForm(employee: Employee) {
    }
}
در اینجا سرویس Http انگیولار به سازنده‌ی کلاس تزریق شده‌است و این نحوه‌ی تعریف سبب می‌شود تا بتوان به پارامتر http، به صورت یک فیلد خصوصی تعریف شده‌ی در سطح کلاس نیز دسترسی پیدا کنیم.
چون این کلاس از ماژول توکار Http استفاده می‌کند، نیاز است این ماژول را نیز به قسمت imports فایل src\app\app.module.ts اضافه کنیم:
import { HttpModule } from "@angular/http";

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    EmployeeModule,
    AppRoutingModule
  ]
اکنون می‌توانیم این سرویس جدید FormPosterService را به سازنده‌ی کامپوننت EmployeeRegisterComponent در فایل src\app\employee\employee-register\employee-register.component.ts تزریق کنیم:
import { FormPosterService } from "../form-poster.service";

export class EmployeeRegisterComponent implements OnInit {

  constructor(private formPoster: FormPosterService) {}

}

در ادامه برای آزمایش برنامه، به ریشه‌ی پروژه وارد شده و دو پنجره‌ی کنسول مجزا را باز کنید. در اولی، دستورات:
>npm install
>ng build --watch
و در دومی، دستورات ذیل را اجرا کنید:
>dotnet restore
>dotnet watch run
دستورات اول کار بازیابی وابستگی‌های سمت کلاینت و سپس ساخت تدریجی برنامه‌ی Angular را دنبال می‌کند. دستورات دوم، وابستگی‌های برنامه‌ی ASP.NET Core را دریافت و نصب کرده و سپس برنامه را در حالت watch ساخته و بر روی پورت 5000 ارائه می‌کند (بدون نیاز به اجرای VS 2017؛ این دستور عمومی است).
به همین جهت برای آزمایش ابتدایی آن، آدرس http://localhost:5000 را در مرورگر باز کنید. برگه‌ی developer tools مرورگر را نیز بررسی کنید تا خطایی در آن ظاهر نشده باشد. برای مثال اگر فراموش کرده باشید تا HttpModule را به app.module اضافه کنید، خطای no provider for HttpModule را مشاهده خواهید کرد.


مدیریت رخداد submit فرم در Angular

تا اینجا کار برپایی تنظیمات اولیه‌ی کار با سرویس Http را انجام دادیم. مرحله‌ی بعد مدیریت رخداد submit فرم است. به همین جهت فایل src\app\employee\employee-register\employee-register.component.html را گشوده و سپس رخدادگردان submit را به فرم آن اضافه کنید:
<form #form="ngForm" (submit)="submitForm(form)" novalidate>
در حین رخدادگردانی submit می‌توان به template reference variable تعریف شده‌ی form# برای دسترسی به وهله‌ای از ngForm نیز کمک گرفت.
export class EmployeeRegisterComponent implements OnInit {
  submitForm(form: NgForm) {
    console.log(this.model);
    console.log(form.value);
  }
}
امضای متد submitForm را در اینجا مشاهده می‌کنید. form دریافتی آن از نوع NgForm است که در ابتدای فایل import شده‌است.
در همین حال اگر بر روی دکمه‌ی ok کلیک کنیم، چنین خروجی را در کنسول developer مروگر می‌توان مشاهده کرد:


اولین مورد، محتوای this.model است و دومی محتوای form.value را گزارش کرده‌است. همانطور که مشاهده می‌کنید، مقدار form.value بسیار شبیه است به وهله‌ای از مدلی که در سطح کلاس تعریف کرده‌ایم و این مقدار همواره توسط Angular نگهداری و مدیریت می‌شود. بنابراین حتما الزامی نیست تا مدلی را جهت کار با فرم‌های مبتنی بر قالب‌ها به صورت جداگانه‌ای تهیه کرد. توسط شیء form نیز می‌توان به تمام اطلاعات فیلدها دسترسی یافت.


تکمیل سرویس ارسال اطلاعات به سرور

در ادامه می‌خواهیم اطلاعات مدل فرم را به سرور ارسال کنیم. برای این منظور سرویس FormPoster را به صورت ذیل تکمیل می‌کنیم:
import { Injectable } from "@angular/core";
import { Http, Response, Headers, RequestOptions } from "@angular/http";

import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/do";
import "rxjs/add/operator/catch";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/map";
import "rxjs/add/observable/of";

import { Employee } from "./employee";

@Injectable()
export class FormPosterService {
  private baseUrl = "api/employee";

  constructor(private http: Http) {}

  private extractData(res: Response) {
    const body = res.json();
    return body.fields || {};
  }

  private handleError(error: Response): Observable<any> {
    console.error("observable error: ", error);
    return Observable.throw(error.statusText);
  }

  postEmployeeForm(employee: Employee): Observable<Employee> {
    const body = JSON.stringify(employee);
    const headers = new Headers({ "Content-Type": "application/json" });
    const options = new RequestOptions({ headers: headers });

    return this.http
      .post(this.baseUrl, body, options)
      .map(this.extractData)
      .catch(this.handleError);
  }
}
برای کار با Observables یا می‌توان نوشت 'import 'rxjs/Rx که تمام بسته‌ی RxJS را import می‌کند، یا همانند این مثال بهتر است تنها اپراتورهایی را که به آن‌ها نیاز پیدا می‌کنیم، import نمائیم. به این ترتیب حجم نهایی ارائه‌ی برنامه نیز کاهش خواهد یافت.
در متد postEmployeeForm، ابتدا توسط JSON.stringify محتوای شیء کارمند encode می‌شود. البته متد post اینکار را به صورت توکار نیز می‌تواند مدیریت کند. سپس ذکر هدر مناسب در اینجا الزامی است تا در سمت سرور بتوانیم اطلاعات دریافتی را به شیء متناظری نگاشت کنیم. در غیراینصورت model binder سمت سرور نمی‌داند که چه نوع فرمتی را دریافت کرده‌است و چه نوع decoding را باید انجام دهد.
در قسمت map، کار بررسی اطلاعات دریافتی از سرور را انجام خواهیم داد و اگر در این بین خطایی وجود داشت، توسط متد handleError در کنسول developer مرورگر نمایش داده می‌شود.
خروجی متد postEmployeeForm یک Observable است. بنابراین تا زمانیکه یک subscriber نداشته باشد، اجرا نخواهد شد. به همین جهت به کلاس EmployeeRegisterComponent مراجعه کرده و متد submitForm را به نحو ذیل تکمیل می‌کنیم:
  submitForm(form: NgForm) {
    console.log(this.model);
    console.log(form.value);

    // validate form
    this.validatePrimaryLanguage(this.model.primaryLanguage);
    if (this.hasPrimaryLanguageError) {
      return;
    }

    this.formPoster
      .postEmployeeForm(this.model)
      .subscribe(
        data => console.log("success: ", data),
        err => console.log("error: ", err)
      );
  }
در اینجا ابتدا اعتبارسنجی سفارشی drop down را که در قسمت قبل بررسی کردیم، قرار داده‌ایم. پس از آن متد postEmployeeForm سرویس formPoster فراخوانی شده‌است و در اینجا کار subscribe به نتیجه‌ی عملیات صورت گرفته‌است که می‌تواند حاوی اطلاعاتی از سمت سرور و یا خطایی در این بین باشد.

یک نکته: اگر علاقمند باشید تا ساختار واقعی شیء NgForm را مشاهده کنید، در ابتدای متد فوق، console.log(form.form) را فراخوانی کنید و سپس شیء حاصل را در کنسول developer مرورگر بررسی نمائید.


تکمیل Web API برنامه‌ی ASP.NET Core جهت دریافت اطلاعات از کلاینت‌ها

در ابتدای سرویس formPoster، یک چنین تعریفی را داریم:
export class FormPosterService {
  private baseUrl = "api/employee";
به همین جهت نیاز است سرویس Web API سمت سرور خود را بر این مبنا تکمیل کنیم.
ابتدا مدل زیر را به پروژه‌ی ASP.NET Core جاری، معادل نمونه‌ی تایپ‌اسکریپتی سمت کلاینت آن اضافه می‌کنیم. البته در اینجا یک Id نیز اضافه شده‌است:
namespace AngularTemplateDrivenFormsLab.Models
{
    public class Employee
    {
        public int Id { set; get; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool IsFullTime { get; set; }
        public string PaymentType { get; set; }
        public string PrimaryLanguage { get; set; }
    }
}

سپس کنترلر جدید EmployeeController را با محتوای ذیل اضافه خواهیم کرد:
using Microsoft.AspNetCore.Mvc;
using AngularTemplateDrivenFormsLab.Models;

namespace AngularTemplateDrivenFormsLab.Controllers
{
    [Route("api/[controller]")]
    public class EmployeeController : Controller
    {
        public IActionResult Post([FromBody] Employee model)
        {
            //todo: save model

            model.Id = 100;
            return Created("", new { fields = model });
        }
    }
}
این کنترلر با شیوه‌ی Web API تعریف شده‌است. مسیریابی آن با api شروع می‌شود تا با مسیر baseUrl سرویس formPoster تطابق پیدا کند.
در اینجا پس از ثبت فرضی مدل، Id آن به همراه اطلاعات مدل، به نحوی که ملاحظه می‌کنید، بازگشت داده شده‌است. این نوع خروجی، یک چنین JSON ایی را تولید می‌کند:
{"fields":{"id":100,"firstName":"Vahid","lastName":"N","isFullTime":true,"paymentType":"FullTime","primaryLanguage":"Persian"}}
به همین جهت است که در متد extractData، دسترسی به body.fields را مشاهده می‌کنید. این fields در اینجا دربرگیرنده‌ی اطلاعات بازگشتی از سرور است (نام آن دلخواه است و درصورت تغییر آن در سمت سرو، باید این نام را در متد extractData نیز اصلاح کنید).
  private extractData(res: Response) {
    const body = res.json();
    return body.fields || {};
  }
اکنون اگر برنامه را با دستورات dotnet watch build و ng build --watch اجرا کنیم، بر روی پورت 5000 قابل دسترسی خواهد بود و پس از ارسال فرم به سرور، چنین خروجی را می‌توان در کنسول developer مرورگر مشاهده کرد:


نمایش success به همراه شیءایی که از سمت سرور دریافت شده‌است؛ که حاصل اجرای سطر ذیل در متد submitForm است:
 data => console.log("success: ", data)
همانطور که مشاهده می‌کنید، این شیء به همراه Id نیز هست. بنابراین درصورت نیاز به آن در سمت کلاینت، خاصیت معادل آن‌را به کلاس کارمند اضافه کرده و در همین سطر فوق می‌توان به آن دسترسی یافت.


بارگذاری اطلاعات drop down از سرور

تا اینجا اطلاعات drop down نمایش داده شده از یک آرایه‌ی مشخص سمت کلاینت تامین شدند. در ادامه قصد داریم تا آن‌ها را از سرور دریافت کنیم. به همین جهت اکشن متد ذیل را به کنترلر سمت سرور برنامه اضافه کنید:
[HttpGet("/api/[controller]/[action]")]
public IActionResult Languages()
{
    string[] languages = { "Persian", "English", "Spanish", "Other" };
    return Ok(languages);
}
که برای آزمایش آن می‌توانید مسیر http://localhost:5000/api/employee/languages را جداگانه در مرورگر درخواست کنید.
پس از آن در سمت کلاینت این تغییرات نیاز هستند:
ابتدا به سرویس FormPosterService دو متد ذیل را اضافه می‌کنیم که کار آن‌ها دریافت و پردازش اطلاعات از api/employee/languages سمت سرور هستند:
  private extractLanguages(res: Response) {
    const body = res.json();
    return body || {};
  }

  getLanguages(): Observable<any> {
    return this.http
      .get(`${this.baseUrl}/languages`)
      .map(this.extractLanguages)
      .catch(this.handleError);
  }
اینبار چون خروجی سمت سرور را مانند قبل (متد extractData) داخل فیلدی مانند fields محصور نکردیم، همان body دریافتی بازگشت داده شده‌است.
پس از آن دو تغییر ذیل را نیاز است به EmployeeRegisterComponent اعمال کنیم:
  languages = [];

  ngOnInit() {
    this.formPoster
      .getLanguages()
      .subscribe(
        data => this.languages = data,
        err => console.log("get error: ", err)
      );
  }
ابتدا آرایه‌ی زبان‌ها با یک آرایه‌ی خالی مقدار دهی شده‌است و سپس در متد ngOnInit، کار دریافت اطلاعات آن از سرور، صورت گرفته‌است.

مشکل! ممکن است مدت زمانی طول بکشد تا این اطلاعات از سمت سرور دریافت شوند. در این حالت می‌توان به شکل زیر در فایل employee-register.component.html فرم را تا زمان پر شدن دراپ داون آن مخفی کرد:
<h3 *ngIf="languages.length == 0">Loading...</h3>
<div class="container" *ngIf="languages.length > 0">
در این حالت هر زمانیکه آرایه‌ی زبان‌ها پر شد، loading حذف شده و div نمایان می‌گردد.

کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: angular-template-driven-forms-lab-05.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس به ریشه‌ی پروژه وارد شده و دو پنجره‌ی کنسول مجزا را باز کنید. در اولی دستورات:
>npm install
>ng build --watch
و در دومی دستورات ذیل را اجرا کنید:
>dotnet restore
>dotnet watch run
اکنون می‌توانید برنامه را در آدرس http://localhost:5000 مشاهده و اجرا کنید.