مطالب
آشنایی با الگوی طراحی Builder
سناریوی زیر را در نظر بگیرید:
از شما خواسته شده است تا نحوه‌ی ساخت تلفن همراه را پیاده سازی نمایید. شما در گام اول 2 نوع تلفن همراه را شناسایی نموده‌اید (Android و Windows Phone). پس از شناسایی، احتمالا هر کدام از این انواع را یک کلاس در نظر می‌گیرید و به کمک یک واسط یا کلاس انتزاعی، شروع به ساخت کلاس می‌نمایید، تا در آینده اگر تلفن همراه جدیدی شناسایی شد، راحت‌تر بتوان آن را در پیاده سازی دخیل نمود.
اگر چنین فکر کرده اید باید گفت که 90% با الگوی طراحی Builder آشنا هستید و از آن نیز استفاده می‌کنید؛ بدون اینکه متوجه باشید از این الگو استفاده کرده‌اید. در کدهای زیر این الگو را قدم به قدم بررسی خواهیم نمود.
قدم 1: تلفن همراه چه بخش هایی می‌تواند داشته باشد؟ (برای مثال یک OS دارند، یک Name دارند و یک Screen) همچنین برای اینکه تلفن همراهی بتواند ساخته شود ابتدا بایستی نام آن‌را بدانیم. کدهای زیر همین رویه را تصدیق می‌نمایند:
public class Product
{
        public Product(string name)
        {
            Name = name;
        }
        public string Name { get; set; }
        public string Screen { get; set; }
        public string OS { get; set; }
        public override string ToString()
        {
            return string.Format(Screen + "/" + OS + "/" + Name);
        }
}
یک کلاس ساخته‌ایم و نام آن را Product گذاشتیم. بخش‌های مختلفی را نیز در آن تعریف نموده‌ایم. تابع ToString را برای استفاده‌های بعدی override کرده‌ایم (فعلا نیازی بدان نداریم).
قدم 2: برای ساخت تلفن همراه چه کارهایی باید انجام شود؟ (برای مثال بایستی OS روی آن نصب شود، Screen آن مشخص شود. همچنین بایستی به طریقی بتوانم تلفن همراه ساخته شده‌ی خود را نیز پیدا کنم). کدهای زیر همین رویه را تصدیق می‌نمایند:
    public interface IBuilder
    {
        void BuildScreen();
        void BuildOS();
        Product Product { get; }
    }
یک واسط تعریف کرده‌ایم تا به کمک آن هر تلفن همراهی را که خواستیم بسازیم.
قدم 3: از آنجا که فقط دو نوع تلفن همراه را فعلا شناسایی کرده‌ایم (Android و Windows Phone) نیاز داریم تا این دو تا را بسازیم.
ابتدا تلفن همراه Android را می‌سازیم:
  public class ConcreteBuilder1 : IBuilder
    {
        public Product p;
        public ConcreteBuilder1()
        {
            p = new Product("Android Cell Phone");
        }
        public void BuildScreen()
        {
            p.Screen = "Touch Screen 16 Inch!";
        }

        public void BuildOS()
        {
            p.OS = "Android 4.4";
        }
        public Product Product
        {
            get { return p; }
        }
    }
سپس تلفن همراه Windows Phone را می‌سازیم:
    public class ConcreteBuilder2 : IBuilder
    {
        public Product p;

        public ConcreteBuilder2()
        {
            p = new Product("Windows Phone");
        }
        public void BuildScreen()
        {
            p.Screen = "Touch Screen 32 Inch!";
        }

        public void BuildOS()
        {
            p.OS = "Windows Phone 2014";
        }
        public Product Product
        {
            get { return p; }
        }
    }
قدم 4: اول باید OS نصب شود یا Screen مشخص شود؟ برای اینکه توالی کار را مشخص سازم نیاز به یک کلاس دیگر دارم تا اینکار را انجام دهد:
    public class Director
    {
        public void Construct(IBuilder builder)
        {
            builder.BuildScreen();
            builder.BuildOS();
        }
    }
این کلاس در متد Construct خود یک ورودی از نوع IBuilder می‌گیرد و براساس توالی مورد نظر، شروع به ساخت آن می‌کند.
قدم 5: نهایتا میخواهم به برنامه‌ی خود بگویم که تلفن همراه Android را بسازد:
Director d = new Director();
ConcreteBuilder1 cb1 = new ConcreteBuilder1();
d.Construct(cb1);
Console.WriteLine(cb1.p.ToString());
و به این صورت تلفن همراه من آماده است!
متد ToString در اینجا، همان ToString ابتدای بحث است که آن را  Override کردیم.
به این نکته توجه کنید که اگر یک تلفن همراه جدید شناسایی شود، چه مقدار تغییری در کدها نیاز دارید؟ برای مثال تلفن همراه BlackBerry شناسایی شده‌است. تنها کاری که لازم است این است که یک کلاس بصورت زیر ساخته شود:
    public class BlackBerry: IBuilder
    {
        public Product p;

        public BlackBerry ()
        {
            p = new Product("BlackBerry");
        }
        public void BuildScreen()
        {
            p.Screen = "Touch Screen 8 Inch!";
        }

        public void BuildOS()
        {
            p.OS = "BlackBerry XXX";
        }
        public Product Product
        {
            get { return p; }
        }
    }
مطالب
بومی سازی منابع در پروژه‌های ASP.NET Core Web API
اگر پروژه‌ی ما فقط از یک Web API تشکیل شده و نیاز است در قسمت‌های مختلف آن، مانند کنترلرها، سرویس‌ها، اعتبارسنج‌ها و غیره از منابع بومی شده استفاده شود، می‌توان از یک راه حل ساده‌ی «SharedResource» استفاده کرد؛ با این مزایا و شرایط:
 - تمام تعاریف بومی سازی مورد نیاز برنامه در یک تک فایل SharedResource.fa.resx قرار می‌گیرند. این فایل نیز در یک اسمبلی مستقل از برنامه‌ی اصلی اضافه می‌شود.
 - با استفاده از تزریق سرویس IStringLocalizer می‌توان به کلیدهای فایل SharedResource.fa.resx در هر قسمتی از برنامه‌ی Web API دسترسی یافت.
 - در این بین اگر کلیدی یافت نشد، خطایی با ذکر دقیق جزئیات منبع جستجو شده، لاگ می‌شود.
 - کلیدهای بومی سازی data annotations نیز قابل دریافت از فایل SharedResource.fa.resx می‌باشند.
 
در ادامه روش پیاده سازی یک چنین امکاناتی را بررسی می‌کنیم.
 
 
قرار دادن فایل منبع اشتراکی در اسمبلی ExternalResources

پس از ایجاد پروژه‌ی ابتدایی Web API به نام Core3xSharedResource.WebApi، یک اسمبلی جدید را برای مثال به نام Core3xSharedResource.ExternalResources تعریف کرده و در داخل آن پوشه‌ی جدید Resources را تعریف می‌کنیم. به این پوشه، فایل منبع جدیدی را به نام SharedResource.fa.resx اضافه می‌کنیم. در کنار آن باید یک کلاس خالی به نام SharedResource.cs نیز وجود داشته باشد.

کار با ین فایل (و یا فایل‌های دیگری مانند SharedResource.en.resx) همانند تمام فایل‌های منبع استاندارد است و نکته‌ی خاصی را به همراه ندارد.


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

پس از ایجاد و تکمیل فایل منبع اشتراکی، برای معرفی آن به برنامه، ابتدا کلاس جدید LocalizationConfig را تعریف کرده و در آن متد جدید AddCustomLocalization را به صورت زیر معرفی می‌کنیم:
    public static class LocalizationConfig
    {
        public static IMvcBuilder AddCustomLocalization(this IMvcBuilder mvcBuilder, IServiceCollection services)
        {
            mvcBuilder.AddDataAnnotationsLocalization(options =>
                    {
                        const string resourcesPath = "Resources";
                        string baseName = $"{resourcesPath}.{nameof(SharedResource)}";
                        var location = new AssemblyName(typeof(SharedResource).GetTypeInfo().Assembly.FullName).Name;

                        options.DataAnnotationLocalizerProvider = (type, factory) =>
                        {
                            // to use `SharedResource.fa.resx` file
                            return factory.Create(baseName, location);
                        };
                    });

            services.AddLocalization();
            services.AddScoped<IStringLocalizer>(provider =>
                            provider.GetRequiredService<IStringLocalizer<SharedResource>>());

            services.AddScoped<ISharedResourceService, SharedResourceService>();
            return mvcBuilder;
        }
    }
- در اینجا در ابتدا توسط متد AddDataAnnotationsLocalization، کار معرفی اسمبلی ثالثی که باید تعاریف بومی سازی را از آن دریافت کرد، صورت گرفته‌است.
- سپس با استفاده از متد AddLocalization، سرویس‌های پایه‌ی بومی سازی ASP.NET Core به برنامه اضافه می‌شوند. برای مثال پس از این تعریف اگر در هر جائی از برنامه سرویس <IStringLocalizer<SharedResource را تزریق کنید، می‌توان به مداخل فایل منبع اشتراکی، دسترسی یافت.
- در ادامه امکان تزریق سرویس غیرجنریک IStringLocalizer را نیز میسر کرده‌ایم که تعاریف خودش را از همان سرویس توکار <IStringLocalizer<SharedResource دریافت می‌کند. مزیت اینکار، فراهم شدن امکانات بومی سازی، برای مثال در کتابخانه‌هایی مانند Fluent Validation است که دقیقا از سرویس غیرجنریک IStringLocalizer برای دریافت منابع استفاده می‌کنند.
- در آخر تعریف یک سرویس سفارشی را نیز مشاهده می‌کنید که در ادامه‌ی بحث تکمیل خواهد شد.

هدف از متد AddCustomLocalization فوق، خلوت کردن فایل startup برنامه است. این متد به صورت زیر مورد استفاده قرار می‌گیرد:
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddHttpContextAccessor();
            services.AddControllers().AddCustomLocalization(services);
        }

پس از آن نیاز است میان‌افزار بومی سازی را نیز فعال کرد. متد UseCustomRequestLocalization زیر، اینکار را انجام می‌دهد:
    public static class LocalizationConfig
    {
        public static IApplicationBuilder UseCustomRequestLocalization(this IApplicationBuilder app)
        {
            var requestLocalizationOptions = new RequestLocalizationOptions
            {
                DefaultRequestCulture = new RequestCulture(new CultureInfo("fa-IR")),
                SupportedCultures = new[]
                {
                    new CultureInfo("en-US"),
                    new CultureInfo("fa-IR")
                },
                SupportedUICultures = new[]
                {
                    new CultureInfo("en-US"),
                    new CultureInfo("fa-IR")
                }
            };
            app.UseRequestLocalization(requestLocalizationOptions);
            return app;
        }
    }
محل قرارگیری متد UseCustomRequestLocalization فوق در فایل آغازین برنامه، باید به صورت زیر باید باشد:
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseCustomRequestLocalization();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }


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

در اینجا تعریف RegisterModel را مشاهده می‌کنید که ErrorMessage‌های آن هرچند به ظاهر یک رشته‌ی معمولی هستند، اما در عمل از فایل منبع اشتراکی خوانده می‌شوند:
using System.ComponentModel.DataAnnotations;

namespace Core3xSharedResource.Models.Account
{
    public class RegisterModel
    {
        [Required(ErrorMessage = "Please enter an email address")] // -->> from the shared resources
        [EmailAddress(ErrorMessage = "Please enter a valid email address")] // -->> from the shared resources
        public string Email { get; set; }
    }
}

فایل resx ما دارای یک چنین کلیدهایی است:
<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="&lt;b&gt;Hello&lt;/b&gt;&lt;i&gt; {0}&lt;/i&gt;" xml:space="preserve">
    <value>&lt;b&gt;سلام&lt;/b&gt;&lt;i&gt; {0}&lt;/i&gt;</value>
  </data>
  <data name="About Title" xml:space="preserve">
    <value>درباره</value>
  </data>
  <data name="DNT" xml:space="preserve">
    <value>.NET Tips</value>
  </data>
  <data name="SiteName" xml:space="preserve">
    <value>DNT</value>
  </data>
  <data name="Please enter an email address" xml:space="preserve">
    <value>لطفا ایمیلی را وارد کنید</value>
  </data>
  <data name="Please enter a valid email address" xml:space="preserve">
    <value>لطفا ایمیل معتبری را وارد کنید</value>
  </data>
</root>
یک نکته: در اینجا بهتر است کلیدها را به صورت جملات کامل انگلیسی وارد کرد، تا اگر منبع فارسی معادل آن‌ها یافت نشدند، دقیقا از همان کلید، به عنوان مقدار خروجی سیستم بومی سازی استفاده کند.


آزمایش برنامه

اکنون برنامه‌ی Web API، ‌برای آزمایش آماده‌است. برای مثال در کنترلر زیر، سرویس عمومی IStringLocalizer به سازنده‌ی کلاس تزریق شده‌است و سپس قصد بازگشت مقدار کلید «About Title» را دارد. همچنین خطاهای بومی شده‌ی مدل برنامه را نیز بررسی می‌کنیم:
using Core3xSharedResource.Models.Account;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;

namespace Core3xSharedResource.WebApi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class NormalIStringLocalizerController : ControllerBase
    {
        private readonly IStringLocalizer _localizer;

        public NormalIStringLocalizerController(IStringLocalizer localizer)
        {
            _localizer = localizer;
        }

        [HttpGet]
        public ActionResult<string> Get()
        {
            var localizedString = _localizer["About Title"];
            if (localizedString.ResourceNotFound)
            {
                return NotFound($"The localization resource with ID:`{localizedString.Name}` not found. SearchedLocation: `{localizedString.SearchedLocation}`.");
            }
            return localizedString.Value;
        }

        [HttpPost]
        public ActionResult<RegisterModel> Post(RegisterModel model)
        {
            return model;
        }
    }
}


حالت get را در تصویر فوق مشاهده می‌کنید. در Web API برای تنظیم زبان مورد استفاده می‌توان از هدری به نام Accept-Language استفاده کرد که برای مثال در اینجا به fa تنظیم شده‌است و نتیجه‌ی آن مراجعه به فایل SharedResource.fa.resx خواهد بود. اگر en-us وارد شود، نیاز خواهد بود تا فایل منبع اشتراکی دیگری را تعریف کنید. البته اگر این هدر تنظیم نشود، با توجه به تنظیمات متد UseCustomRequestLocalization، مقدار پیش‌فرض آن همان fa-IR خواهد بود.

حالت post را نیز در تصویر زیر می‌توان مشاهده کرد:


در اینجا چون ایمیل وارد نشده، هر دو خطای تنظیم شده‌ی در مدل برنامه را دریافت کرده‌ایم و این خطاها نیز فارسی هستند. به این معنا که بومی سازی data annotations نیز به درستی کار می‌کند.


تعریف یک سرویس عمومی برای محصور سازی قابلیت‌های بومی سازی، در برنامه‌های Web API

در ادامه تعریف سرویس SharedResourceService را مشاهده می‌کنید که ثبت آن‌را پیشتر انجام دادیم:
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Http;

namespace Core3xSharedResource.Services
{
    public interface ISharedResourceService
    {
        string this[string index] { get; }

        IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures);
        string GetString(string name, params object[] arguments);
        string GetString(string name);
    }

    public class SharedResourceService : ISharedResourceService
    {
        private readonly IStringLocalizer _sharedLocalizer;
        private readonly ILogger<SharedResourceService> _logger;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public SharedResourceService(
            IStringLocalizer sharedHtmlLocalizer,
            IHttpContextAccessor httpContextAccessor,
            ILogger<SharedResourceService> logger
            )
        {
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _sharedLocalizer = sharedHtmlLocalizer ?? throw new ArgumentNullException(nameof(sharedHtmlLocalizer));
            _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
        }

        public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
        {
            return _sharedLocalizer.GetAllStrings(includeParentCultures);
        }

        public string this[string index] => GetString(index);

        public string GetString(string name, params object[] arguments)
        {
            var result = _sharedLocalizer.GetString(name, arguments);
            logError(name, result);
            return result;
        }

        private void logError(string name, LocalizedString result)
        {
            if (result.ResourceNotFound)
            {
                var acceptLanguage = _httpContextAccessor?.HttpContext?.Request?.Headers["Accept-Language"];
                _logger.LogError($"The localization resource with Accept-Language:`{acceptLanguage}` & ID:`{name}` not found. SearchedLocation: `{result.SearchedLocation}`.");
            }
        }

        public string GetString(string name)
        {
            var result = _sharedLocalizer.GetString(name);
            logError(name, result);
            return result;
        }
    }
}
این سرویس نه فقط دسترسی به IStringLocalizer را محصور می‌کند، بلکه در متد logError آن اینبار خطای بسیار مفیدی جهت دیباگ کردن سیستم بومی سازی لاگ خواهد شد. اگر کلیدی یافت نشود، فایلی یافت نشود و یا زبان ارسالی تنظیمی یافت نشود، خطای آن‌را در لاگ‌های برنامه می‌توانید مشاهده کنید که در حالت عادی کار با IStringLocalizer، لاگ نمی‌شوند و همچنین هیچ خطا و یا استثنائی را نیز سبب نمی‌شوند. به همین جهت دیباگ کردن سیستم بومی سازی بدون این لاگ‌ها، تقریبا غیرممکن است. برای مثال مقدار baseNameهایی را که در کدهای این مطلب مشاهده می‌کنید، بر اساس همین لاگ‌ها تشخیص داده شدند و بدون آن‌ها تشکیل این مقادیر غیرممکن بودند.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Core3xSharedResource.zip
مطالب
تولید خودکار کدهای سمت کلاینت بر اساس OpenAPI Specification
در سری «مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger» با نحوه‌ی تولید OpenAPI Specification، بر اساس کنترلرها و اکشن متدهای Web API خود آشنا شدیم و سپس با استفاده از ابزار Swagger-UI، یک رابط کاربری پویا را نیز برای آن تولید و سفارشی سازی کردیم. کاربرد OpenAPI Specification صرفا به مستندسازی یک Web API خلاصه نمی‌شود. بر اساس این استاندارد، ابزارهای متعددی جهت تولید کدهای سمت سرور و سمت کلاینت نیز طراحی شده‌اند که در اینجا نمونه‌ای از آن‌ها را بررسی خواهیم کرد.


تولید خودکار کدها بر اساس OpenAPI Specification

فرض کنید در حال توسعه‌ی برنامه‌ی سمت کلاینت Angular و یا سمت سرور ASP.NET Core ای هستید که هر دوی این‌ها از یک Web API استفاده می‌کنند. همچنین فرض کنید که این Web API را نیز خودتان توسعه می‌دهید. بنابراین حداقل کدی که باید در اینجا به اشتراک گذاشته شود، کدهای کلاس‌های DTO یا Data transfer objects هستند تا این کلاینت‌ها بتوانند اطلاعات Web API را به نحو صحیحی Deserialize کنند و یا برعکس، بتوانند اطلاعات را با فرمت صحیحی به سمت Web API ارسال کنند.
برای مدیریت این مساله می‌توان از دو روش استفاده کرد:
الف) استفاده از یک پروژه‌ی اشتراکی
اگر کدهای مدنظر، سمت سرور باشند، می‌توان یک پروژه‌ی اشتراکی را برای این منظور ایجاد کرد و کدهای DTO را درون آن قرار داد و سپس ارجاعی به آن را در پروژه‌های مختلف، استفاده نمود. به این ترتیب تکرار کدها، کاهش یافته و همچنین تغییرات آن نیز به تمام پروژه‌های استفاده کننده به نحو یکسانی اعمال می‌شوند. در این حالت یک اسمبلی اشتراکی تولید شده و به صورت مستقلی توزیع می‌شود.

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

بدیهی است در هر دو روش، نیاز است بر روی کلاینت و API، کنترل کاملی وجود داشته باشد و بتوان به کدهای آن‌ها دسترسی داشت. به علاوه فایل‌های اشتراکی نیز باید بر اساس Target platform یکسانی تولید شده باشند. در این حالت دیگر نیازی به OpenAPI Specification برای تولید کدهای کلاینت دات نتی خود، نیست.

اما اگر کدهای API مدنظر در دسترس نباشند و یا بر اساس پلتفرم دیگری مانند node.js تولید شده باشد، کار یکپارچه سازی با آن دیگر با به اشتراک گذاری فایل‌های آن میسر نیست. در این حالت اگر این API به همراه یک OpenAPI Specification باشد، می‌توان از آن برای تولید خودکار کدهای کلاینت‌های آن استفاده کرد.


معرفی تعدادی از ابزارهایی که قادرند بر اساس OpenAPI Specification، کد تولید کنند

برای تولید کد از روی OpenAPI Specification، گزینه‌های متعددی در دسترس هستند:

الف) Swagger CodeGen
این ابزار را که جزئی از مجموعه ابزارهای تولید شده‌ی برفراز OpenAPI است، می‌توانید از آدرس swagger-codegen دریافت کنید. البته برای اجرای آن نیاز به Java Runtime است و یا نگارش آنلاین آن نیز در دسترس است: swagger.io
در ابزار آنلاین آن، در منوی generate بالای صفحه، گزینه‌ی تولید کد برای #C نیز موجود است.

ب)  AutoRest
محل دریافت: https://github.com/Azure/autorest
بر اساس node.js کار می‌کند و از طریق خط فرمان، قابل دسترسی است. همچنین این مورد ابزار تامین کننده‌ی گزینه‌ی Add REST client در ویژوال استودیو نیز می‌باشد. اما در کل، امکان تنظیمات آنچنانی را به همراه ندارد.

ج) NSwagStudio
محل دریافت: https://github.com/RSuter/NSwag/wiki/NSwagStudio
همانطور که در مطلب «مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger - قسمت اول - معرفی» نیز عنوان شد، NSwag یکی دیگر از تولید کننده‌های OpenAPI Specification مخصوص پروژه‌های دات نت است. NSwagStudio نیز جزئی از این مجموعه است که به کمک آن می‌توان کدهای کلاینت‌ها و DTOها را بر اساس OpenAPI Spec تولید کرد. همچنین امکان تنظیمات قابل توجهی را در مورد نحوه‌ی تولید کدهای نهایی به همراه دارد.


استفاده از NSwagStudio برای تولید کدهای DTOها

در اینجا از همان برنامه‌ای که در سری «مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger» بررسی کردیم، استفاده خواهیم کرد. بنابراین این برنامه، از پیش تنظیم شده‌است و هم اکنون به همراه یک تولید کننده‌ی OpenAPI Specification نیز می‌باشد. آن‌را اجرا کنید تا بتوان به OpenAPI Specification تولیدی آن در آدرس زیر دسترسی یافت:
 https://localhost:5001/swagger/LibraryOpenAPISpecification/swagger.json
سپس فایل msi مخصوص NSwagStudio را نیز از لینک آن در Github دریافت، نصب و اجرا کنید.


مطابق تصویر، ابتدا آدرس Swagger Specification URL یا همان آدرس فوق را وارد کنید. سپس فضای نام دلخواهی را وارد کرده و گزینه‌ی تولید کلاس‌های کلاینت را فعلا انتخاب نکنید. در لیست تنظیمات آن، گزینه‌ی Class Style نیز مهم است. برای مثال برای پروژه‌های ASP.NET Core حالت POCO را انتخاب کنید (plain old clr objects) و برای پروژه‌های مبتنی بر XAML، گزینه‌ی Inpc مناسب‌تر است چون RaisePropertyChanged‌ها را هم تولید می‌کند. در آخر بر روی دکمه‌ی Generate Outputs کلیک کنید تا خروجی ذیل حاصل شود:


یا می‌توان این خروجی را copy/paste کرد و یا می‌توان در برگه‌ی Settings، در انتهای لیست آن، مقدار output file path را مشخص کرد و سپس بر روی دکمه‌ی Generate files کلیک نمود تا فایل معادل آن تولید شود.


استفاده از NSwagStudio برای تولید کدهای کلاینت Angular استفاده کننده‌ی از API

NSwagStudio امکان تولید یک TypeScript Client را نیز دارد:

در اینجا ابتدا TypeScript Client را انتخاب می‌کنیم و سپس در تنظیمات آن، قالب Angular را انتخاب کرده و نگارش RxJS آن‌را نیز، 6 انتخاب می‌کنیم. در آخر بر روی Generate outputs کلیک می‌کنیم:


نکته‌ی جالب این خروجی، دقت داشتن به status codes درج شده‌ی در OpenAPI Spec است که در قسمت‌های چهارم و پنجم سری «مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger» آن‌ها را بررسی کردیم.
در اینجا نه تنها سرویسی جهت تعامل با API ما تولید شده‌است، بلکه معادل تایپ‌اسکریپتی DTOهای برنامه را نیز تولید کرده‌است:

اشتراک‌ها
Visual Studio 2015 CTP 6 منتشر شد.
  • UI Debugging Tools for XAML
  • Single Sign-In
  • CodeLens
  • Code Maps
  • Diagnostics Tools
  • Exception Settings
  • JavaScript Editor
  • Unit Tests
  • Visual Studio Emulator for Android
  • Visual Studio C++ for Cross-Platform Mobile Development
  • ASP.NET
  • Visual C++

Other changes:

Bug Fixes & Known Issues
Related releases:

Team Foundation Server 2015 CTP 

Visual Studio 2015 CTP 6 منتشر شد.
مطالب
بررسی تغییرات Blazor 8x - قسمت اول - معرفی SSR
از لحاظ تاریخی، Blazor به همراه دو حالت اصلی است:
- Blazor Server، که در آن یک اتصال SignalR، بین مرورگر کاربر و سرور، برقرار شده و سرور حالات مختلف این جلسه‌ی کاری را مدیریت می‌کند. آغاز این حالت، بسیار سریع است؛ اما وجود اتصال دائم SignalR در آن ضروری است. نیاز به وجود این اتصال دائم، با تعداد بالای کاربر می‌تواند کارآیی سرور را تحت تاثیر قرار دهد.
- Blazor WASM: در این حالت کل برنامه‌ی Blazor، درون مرورگر کاربر اجرا می‌شود و برای اینکار الزاما نیازی به سرور ندارد؛ اما آغاز اولیه‌ی آن به علت نیاز به بارگذاری کل برنامه درون مرورگر کاربر، اندکی کند است. اتصال این روش با سرور، از طریق روش‌های متداول کار با Web API صورت می‌گیرد و نیازی به اتصال دائم SignalR را ندارد.

دات نت 8، دو تغییر اساسی را در اینجا ارائه می‌دهد:
- در اینجا حالت جدیدی به نام SSR یا Static Server Rendering ارائه شده‌است (به آن Server-side rendering هم می‌گویند). در این حالت نه WASM ای درون مرورگر کاربر اجرا می‌شود و نه اتصال دائم SignalR ای برای کار با آن نیاز است! در این حالت برنامه تقریبا همانند یک MVC Razor application سنتی کار می‌کند؛ یعنی سرور، کار رندر نهایی HTML قابل ارائه‌ی به کاربر را انجام داده و آن‌را به سمت مرورگر، برای نمایش ارسال می‌کند و همچنین سرور، هیچ حالتی را هم از برنامه ذخیره نمی‌کند و به‌علاوه، کلاینت نیز نیازی به دریافت کل برنامه را در ابتدای کار ندارد (هم آغاز و نمایش سریعی را دارد و هم نیاز به منابع کمتری را در سمت سرور برای اجرا دارد).
- تغییر مهم دیگری که در دات نت 8 صورت گرفته، امکان ترکیب کردن حالت‌های مختلف رندر صفحات، در برنامه‌های Blazor است. یعنی می‌توان یک صفحه‌ی SSR را داشت که تنها قسمت کوچکی از آن بر اساس معماری Blazor Server کار کند (قسمت‌‌های اصطلاحا interactive یا تعاملی آن). یا حتی در یک برنامه، امکان ترکیب Blazor Server و Blazor WASM نیز وجود دارد.

این‌ها عناوین موارد جدیدی هستند که در این سری به تفصیل بررسی خواهیم کرد.


تاریخچه‌ی نمایش صفحات وب در مرورگرها

در ابتدای ارائه‌ی وب، سرورها، ابتدا درخواستی را از طرف مرورگر کلاینت دریافت می‌کردند و در پاسخ به آن، HTML ای را تولید و بازگشت می‌دادند. حاصل آن، نمایش یک صفحه‌ی استاتیک non-interactive بود (غیرتعاملی). علت تاکید بر روی واژه‌ی interactive (تعاملی)، بکارگیری گسترده‌ی آن در نگارش جدید Blazor است؛ تا حدی که ایجاد قالب‌های جدید آغازین برنامه‌های آن، با تنظیم این گزینه همراه است. برای مشاهده‌ی آن، پس از نصب SDK جدید دات نت، دستور dotnet new blazor --help را صادر کنید.
سپس JavaScript از راه رسید و هدف آن، افزودن interactivity به صفحات سمت کاربر بود تا بتوان بر اساس تعاملات و ورودی‌های کاربر، تغییراتی را بر روی محتوای صفحه اعمال کرد. در ادامه JavaScript این امکان را یافت تا بتواند درخواست‌هایی را به سمت سرور ارسال کند و بر اساس خروجی دریافتی، قسمت‌هایی از صفحه‌ی جاری استاتیک را به صورت پویا تغییر دهد.
در ادامه با بالارفتن توانمندی‌های سخت‌افزاری و همچنین توسعه‌ی کتابخانه‌های جاوااسکریپتی، به برنامه‌های تک صفحه‌ای کاملا پویا و interactive رسیدیم که به آن‌ها SPA گفته می‌شود (Single-page applications)؛ از این دست کتابخانه‌ها می‌توان به Backbone اشاره کرد و پس از آن به React و Angular. برنامه‌های Blazor نیز اخیرا به این جمع اضافه شده‌اند.
اما ... اخیرا توسعه دهنده‌ها به این نتیجه رسیده‌اند که SPAها برای تمام امور، مناسب و یا حتی الزامی نیستند. گاهی از اوقات ما فقط نیاز داریم تا محتوایی را خیلی سریع و بهینه تولید و بازگشت دهیم؛ مانند نمایش لیست اخبار، به هزاران دنبال کننده، با حداقل مصرف منابع و در همین حال نیاز به interactivity در بعضی از قسمت‌های خاص نیز احساس می‌شود. این رویه‌ای است که در تعدادی از فریم‌ورک‌های جدید و مدرن جاوااسکریپتی مانند Astro در پیش گرفته شده‌است؛ در آن‌ها ترکیبی از رندر سمت سرور، به همراه interactivity سمت کاربر، مشاهده می‌شود. برای مثال این امکان را فراهم می‌کنند تا محتوای قسمتی از صفحه را در سمت سرور تهیه و رندر کنید، یا قسمتی از صفحه (یک کامپوننت خاص)، به صورت interactive فعال شود. ترکیب این دو مورد، دقیقا هدف اصلی Blazor، در دات نت 8 است. برای مثال فرض کنید می‌خواهید برنامه و سایتی را طراحی کنید که چند صفحه‌ی آغازین آن، بدون هیچگونه تعاملی با کاربر هستند و باید سریع و SEO friendly باشند. همچنین تعدادی از صفحات آن هم قرار است فقط یک سری محتوای ثابت را نمایش دهند، اما در قسمت‌های خاصی از آن نیاز به تعامل با کاربر است.


معرفی Blazor یکپارچه در دات نت 8

مهم‌ترین تغییر Blazor در دات نت 8، یکپارچه شدن حالت‌های مختلف رندر آن در سمت سرور است. تغییرات زیاد رخ داده‌اند تا امکان داشتن Server-side rendering یا SSR به همراه قابلیت فعال سازی interactivity به ازای هر کامپوننت دلخواه که به آن حالت‌های رندر (Render modes) گفته می‌شود، میسر شوند. در اساس، این روش جدید، همان Blazor Server بهبود یافته‌است که حالت SSR، حالت پیش‌فرض آن است. در کنار آن قابلیت‌های راهبری (navigation)، نیز بهبود یافته‌اند تا برنامه‌های SSR همانند برنامه‌های SPA به‌نظر برسند.

در دات نت 8، ASP.NET Core و Blazor نیز کاملا یکپارچه شده‌اند. در این حالت برنامه‌های Blazor Server می‌توانند همانند برنامه‌های MVC Razor Pages متداول، با کمک قابلیت SSR، صفحات غیر interactive ای را رندر کنند؛ البته به کمک کامپوننت‌های Razor. مزیت آن نسبت به  MVC Razor Pages این است که اکنون می‌توانید هر کامپوننت مجزایی از صفحه را نیز کاملا interactive کنید.
در نگارش‌های قبلی Blazor، برنامه‌های Blazor Server حتی برای شروع کار نیاز به یک صفحه‌ی Razor Pages آغازین داشتند، اما دیگر نیازی به این مورد با دات نت  8 نیست؛ چون ASP.NET Core 8x می‌تواند کامپوننت‌های Razor را نیز به صورت HTML خالص بازگشت دهد و یا Minimal API آن به همراه خروجی new RazorComponentResult نیز شده‌است. در حالت SSR، حتی سیستم مسیریابی ASP.NET Core نیز با Blazor یکی شده‌است.

البته این تغییرات، حالت‌های خالص Blazor WebAssembly و یا MAUI Blazor Hybrid را تحت تاثیر قرار نمی‌دهند؛ اما بدیهی است تمام آن‌ها از سایر قابلیت‌های جدید اضافه شده نیز بهره‌مند هستند.


معرفی حالت‌های مختلف رندر Blazor در دات نت 8

یک برنامه‌ی جدید 8x Blazor، در اساس بر روی سرور رندر می‌شود (همان SSR). اما همانطور که عنوان شد، این SSR ارائه شده‌ی توسط Blazor، یک قابلیت مهم را نسبت به MVC Razor pages دارد و آن هم امکان فعالسازی interactivity، به ازای کامپوننت‌ها و قسمت‌های کوچکی از صفحه است که واقعا نیاز است تعاملی باشند. فعالسازی آن هم بسیار ساده، یک‌دست و یکپارچه است:
@* For being rendered on the server *@
<Counter @rendermode="@InteractiveServer" />

@* For running in WebAssembly *@
<Counter @rendermode="@InteractiveWebAssembly" />
در این حالت می‌توان مشخص کرد که آیا قرار است این کامپوننت خاصی که در قسمتی از صفحه‌ی جاری قرار است رندر شود، نیاز است به کمک فناوری وب‌اسمبلی اجرا شود و یا قرار است بر روی سرور رندر شود؟

این تعاریف حالت رندر را توسط دایرکتیوها نیز می‌توان به ازای هر کامپوننت مجزا، مشخص کرد (یکی از این دو حالت باید بکار گرفته شود):
@rendermode InteractiveServer

@rendermode InteractiveWebAssembly
حالت رندر مشخص شده، توسط زیرکامپوننت‌های تشکیل دهنده‌ی این کامپوننت‌ها نیز به ارث برده می‌شوند؛ اما امکان ترکیب آن‌ها با هم نیست. یعنی اگر حالت رندر را InteractiveServer انتخاب کردید، زیرکامپوننت‌های تشکیل دهنده‌ی آن نمی‌توانند حالت دیگری را انتخاب کنند.
امکان اعمال این ویژگی‌ها به مسیریاب برنامه نیز وجود دارد که در این حالت کل برنامه را interactive می‌کند. اما در حالت پیش‌فرض، برنامه‌ای که ایجاد می‌شود فاقد تنظیمات تعاملی در ریشه‌ی اصلی آن است.


معرفی حالت رندر خودکار در Blazor 8x

یکی دیگر از حالت‌های رندر معرفی شده‌ی در Blazor 8x، حالت Auto است:
<Counter @rendermode="@InteractiveAuto" />
این حالت رندر، به صورت پیش‌فرض از WebAssembly استفاده می‌کند؛ اما فقط زمانیکه فایل‌های مرتبط با آن کاملا دریافت شده‌باشند. یعنی در ابتدای کار برای ارائه‌ی امکانات تعاملی، از حالت سریع و سبک InteractiveServer استفاده می‌کند؛ اما در پشت صحنه مشغول به دریافت فایل‌های مرتبط با نگارش وب‌اسمبلی کامپوننت فوق خواهد شد. پس از بارگذاری و کش شدن این فایل‌ها، برای بارهای بعدی رندر، فقط از حالت وب‌اسمبلی استفاده می‌کند.


معرفی حالت رندر Streaming در Blazor 8x

در بار اول بارگذاری صفحات، ممکن است دریافت اطلاعات مرتبط با آن کمی کند و با وقفه باشند. در این حالت برای اینکه برنامه‌های SSR یک صفحه‌ی خالی را نمایش ندهند، می‌توان در ابتدا با استفاده از حالت رندر جدید StreamRendering، حداقل قالب صفحه را نمایش داد و سپس اصل اطلاعات را:
@attribute [StreamRendering(prerender: true)]
این روش، از HTTP Streaming در پشت صحنه استفاده کرده و مرحله به مرحله قسمت‌های تکمیل شده را به سمت مرورگر کاربر، برای نمایش نهایی ارسال می‌کند.


جزئیات بیشتر نحوه‌ی کار با این حالات را در قسمت‌های بعدی بررسی خواهیم کرد.


نتیجه گیری:

روش‌های جدید رندر ارائه شده‌ی در Blazor 8x، برای موارد زیر مفید هستند:
- زمانیکه قسمت عمده‌ای از برنامه‌ی شما بر روی سرور اجرا می‌شود.
- زمانیکه خروجی اصلی برنامه‌ی شما بیشتر حاوی محتواهای ثابت است؛ مانند CMSها.
- زمانیکه می‌خواهید صفحات شما قابل ایندکس شدن توسط موتورهای جستجو باشند و مباحث SEO برای شما مهم است.
- زمانیکه نیاز به مقدار کمی امکانات تعاملی دارید و فقط قسمت‌های کوچکی از صفحه قرار است تعاملی باشند. برای مثال فقط قرار است قسمت کوچکی از یک صفحه‌ی نمایش مقاله‌ای از یک بلاگ، به همراه امکان رای دادن به آن مطلب (تنها قسمت «تعاملی» صفحه) باشد.
- و یا زمانیکه می‌خواهید MVC Razor Pages را با یک فناوری جدید که امکانات بیشتری را در اختیار شما قرار می‌دهد، جایگزین کنید.
مطالب
کار با Docker بر روی ویندوز - قسمت سوم - نصب Docker بر روی ویندوز سرور
در قسمت قبل، Docker for Windows را بر روی ویندوز 10 نصب کردیم تا بتوانیم از هر دوی Linux Containers و Windows Containers استفاده کنیم. در این قسمت، نحوه‌ی نصب Docker را بر روی ویندوز سرور، صرفا جهت اجرای Windows Containers، بررسی می‌کنیم؛ از این جهت که در دنیای واقعی، عموما Linux Containers را بر روی سرورهای لینوکسی و Windows Containers را بر روی سرورهای ویندوزی اجرا می‌کنند.


Docker for Windows چگونه از هر دوی کانتینرهای ویندوزی و لینوکسی پشتیبانی می‌کند؟

زمانیکه docker for windows را اجرا می‌کنیم، سرویسی را ایجاد می‌کند که سبب اجرای پروسه‌ی ویژه‌ای به نام com.docker.proxy.exe می‌شود:


هنگامیکه برای مثال فرمان docker run nginx را توسط Docker CLI اجرا می‌کنیم، Docker CLI از طریق واسط یاد شده، دستورات را به MobyLinuxVM منتقل می‌کند. به این صورت است که امکان اجرای Linux Containers، بر روی ویندوز میسر می‌شوند:


اکنون اگر به Windows Containers سوئیچ کنیم (از طریق کلیک راست بر روی آیکن Docker در قسمت Tray Icons ویندوز)، پروسه‌ی dockerd.exe یا docker daemon شروع به کار خواهد کرد:


اینبار اگر مجددا از Docker CLI برای اجرای مثلا IIS Container استفاده کنیم، دستور ما از طریق واسط‌های com.docker.proxy و dockerd‌، به کانتینر ویندوزی منتقل و اجرا می‌شود:



نگاهی به معماری Docker بر روی ویندوز سرور

داکر بر روی ویندوز سرور، تنها به همراه موتور مدیریت کننده‌ی Windows Containers است:


در اینجا با صدور فرمان‌های Docker CLI، پیام‌ها مستقیما به dockerd یا موتور داکر بر روی ویندوز سرور ارسال شده و سپس کار اجرا و مدیریت یک Windows Container انجام می‌شود.


نصب Docker بر روی ویندوز سرور

جزئیات مفصل و به روز Windows Containers را همواره می‌توانید در این آدرس در سایت مستندات مجازی سازی مایکروسافت مطالعه کنید (قسمت Container Host Deployment - Windows Server آن). پیشنیاز کار با آن نیز نصب حداقل ویندوز سرور 2016 می‌باشد و بهتر است تمام به روز رسانی‌های آن‌را نیز نصب کرده باشید؛ چون تعدادی از بهبودهای کار با کانتینرهای آن، به همراه به روز رسانی‌ها آن ارائه شده‌اند.
برای شروع به نصب، نیاز است کنسول PowerShell ویندوز را با دسترسی Admin اجرا کنید.
سپس اولین دستوراتی را که نیاز است اجرا کنید، کار نصب موتور Docker و CLI آن‌را به صورت خودکار بر روی ویندوز سرور انجام می‌دهند:
Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
Install-Package -Name docker -ProviderName DockerMsftProvider
Restart-Computer -Force
- که پس از نصب و ری‌استارت سیستم، نتیجه‌ی آن‌را در پوشه‌ی c:\Program Files\Docker می‌توانید ملاحظه کنید.
- به علاوه اگر دستور *get-service *docker را در کنسول PowerShell صادر کنید، مشاهده خواهید کرد که سرویس جدیدی را به نام Docker نیز نصب و راه اندازی کرده‌است که به dockerd.exe اشاره می‌کند.
- و یا اگر در کنسول PowerShell دستور docker را صادر کنید، ملاحظه خواهید کرد که CLI آن، فعال و قابل استفاده‌است. برای مثال، دستور docker version را صادر کنید تا بتوانید نگارش docker نصب شده را ملاحظه نمائید.


اجرای Image مخصوص NET Core. بر روی ویندوز سرور

تگ‌های مختلف Image مخصوص NET Core. را در اینجا ملاحظه می‌کنید. در ادامه قصد داریم tag مرتبط با nanoserver آن‌را نصب کنیم (با حجم 802MB):
docker run microsoft/dotnet:nanoserver
زمانیکه این دستور را اجرا می‌کنیم، پس از اجرای آن، ابتدا یک \:C را نمایش می‌دهد و بعد خاتمه یافته و به command prompt بازگشت داده می‌شویم. برای مشاهده‌ی علت آن، اگر دستور docker ps -a را اجرا کنیم، در ستون command آن، قسمتی از دستوری را که اجرا کرده‌است، می‌توان مشاهده کرد. برای مشاهده‌ی کامل این دستور، نیاز است دستور docker ps -a --no-trunc را اجرا کنیم. در اینجا سوئیچ no-trunc به معنای no truncate است یا عدم حذف قسمت انتهایی یک دستور طولانی. در این حالت مشاهده خواهیم کرد که این دستور، کار اجرای cmd.exe واقع در پوشه‌ی ویندوز را انجام می‌دهد (یا همان command prompt معمولی ویندوز). چون دستور docker run فوق به آن متصل نشده‌است، این پروسه ابتدا \:c را نمایش می‌دهد و سپس خاتمه پیدا می‌کند. برای رفع این مشکل، از interactive command که در قسمت قبل توضیح دادیم، استفاده خواهیم کرد:
docker run -it microsoft/dotnet:nanoserver
اینبار اگر این دستور را اجرا کنیم، به command prompt آغاز شده‌ی توسط آن، متصل خواهیم شد. اکنون اگر در همینجا (داخل container در حال اجرا) دستور dotnet --info را صادر کنید، می‌توان مشخصات NET Core SDK. نصب شده را مشاهده کرد. برای خروج از آن نیز دستور exit را صادر کنید.


چرا حجم Image مخصوص NET Core. نگارش nanoserver آن حدود 800 مگابایت است؟

در مثال قبلی، دسترسی به command prompt مجزایی نسبت به command prompt اصلی سیستم، در داخل یک container، شاید اندکی غیر منتظره بود و اکنون این سؤال مطرح می‌شود که یک image، شامل چه چیزهایی است؟
یک image شاید در ابتدای کار صرفا شامل فایل‌های اجرایی یک برنامه‌ی خاص به نظر برسد؛ اما زمانیکه قرار است تبدیل به یک container قابل اجرا شود، شامل بسیاری از فایل‌های دیگر نیز خواهد شد. برای درک این موضوع نیاز است لایه‌های نرم افزاری که یک سیستم را تشکیل می‌دهند، بررسی کنیم:


در این تصویر از پایین‌ترین لایه‌ای را که با سخت افزار ارتباط برقرار می‌کند تا بالاترین لایه‌ی موجود نرم افزاری را مشاهده می‌کنید. دراینجا هر چیزی را که در ناحیه‌ی کرنل قرار نمی‌گیرد، User Space می‌نامند. برنامه‌های قرار گرفته‌ی در User Space برای کار با سخت افزار نیاز است با کرنل ارتباط برقرار کنند و برای این منظور از System Calls استفاده می‌کنند که عموما کتابخانه‌هایی هستند که جزئی از سیستم عامل می‌باشند؛ مانند API ویندوز. برای مثال MongoDB توسط Win32 API و System Calls، فرامینی را به کرنل منتقل می‌کند.
در روش متداول توزیع و نصب نرم افزار، ما عموما همان بالاترین سطح را توزیع و نصب می‌کنیم؛ برای مثال خود MongoDB را. در اینجا نصاب MongoDB فرض می‌کند که در سیستم جاری، تمام لایه‌های دیگر، موجود و آماده‌ی استفاده هستند و اگر اینگونه نباشد، به مشکل برخواهد خورد و اجرا نمی‌شود. برای اجتناب از یک چنین مشکلاتی مانند عدم حضور وابستگی‌هایی که یک برنامه برای اجرا نیاز دارد، imageهای docker، نحوه‌ی توزیع نرم افزارها را تغییر داده‌اند. اینبار یک image بجای توزیع فقط MongoDB، شامل تمام قسمت‌های مورد نیاز User Space نیز هست:


به این ترتیب دیگر مشکلاتی مانند عدم وجود یک وابستگی یا حتی وجود یک وابستگی غیرسازگار با نرم افزار مدنظر، وجود نخواهند داشت. حتی می‌توان تصویر فوق را به صورت زیر نیز خلاصه کرد:


به همین جهت بود که برای مثال در قسمت قبل موفق شدیم IIS مخصوص ویندوز سرور با تگ nanoserver را بر روی ویندوز 10 که بسیاری از وابستگی‌های مرتبط را به همراه ندارد، با موفقیت اجرا کنیم.
به علاوه چون یک container صرفا به معنای یک running process از یک image است، هر فایل اجرایی داخل آن image را نیز می‌توان به صورت یک container اجرا کرد؛ مانند cmd.exe داخل image مرتبط با NET Core. که آن‌را بررسی کردیم.


کارآیی Docker Containers نسبت به ماشین‌های مجازی بسیار بیشتر است

مزیت دیگر یک چنین توزیعی این است که اگر چندین container در حال اجرا را داشته باشیم:


 در نهایت تمام آن‌ها فقط با یک لایه‌ی کرنل کار می‌کنند و آن هم کرنل اصلی سیستم جاری است. به همین جهت کارآیی docker containers نسبت به ماشین‌های مجازی بیشتر است؛ چون هر ماشین مجازی، کرنل مجازی خاص خودش را نسبت به یک ماشین مجازی در حال اجرای دیگر دارد. در اینجا برای ایجاد یک لایه ایزوله‌ی اجرای برنامه‌ها، تنها کافی است یک container جدید را اجرا کنیم و در این حالت وارد فاز بوت شدن یک سیستم عامل کامل، مانند ماشین‌های مجازی نمی‌شویم.

شاید مطابق تصویر فوق اینطور به نظر برسد که هرچند تمام این containers از یک کرنل استفاده می‌کنند، اما اگر قرار باشد هر کدام OS Apps & Libs خاص خودشان را در حافظه بارگذاری کنند، با کمبود شدید منابع روبرو شویم. دقیقا مانند حالتیکه چند ماشین مجازی را اجرا کرده‌ایم و دیگر سیستم اصلی قادر به پاسخگویی به درخواست‌های رسیده به علت کمبود منابع نیست. اما در واقعیت، یک image داکر، از لایه‌های مختلفی تشکیل می‌شود که فقط خواندنی هستند و غیرقابل تغییر و زمانیکه docker یک لایه‌ی فقط خواندنی را در حافظه بارگذاری کرد، اگر container دیگری، از همان لایه‌ی تعریف شده‌، در image خود نیز استفاده می‌کند، لایه‌ی بارگذاری شده‌ی فقط خواندنی در حال اجرای موجود را با آن به اشتراک می‌گذارد (مانند تصویر زیر). به این ترتیب میزان مصرف منابع docker containers نسبت به ماشین‌های مجازی بسیار کمتر است:



روش کنترل پروسه‌ای که درون یک کانتینر اجرا می‌شود

با اجرای دستور docker run -it microsoft/dotnet:nanoserver ابتدا به command prompt داخلی و مخصوص این container منتقل می‌شویم و سپس می‌توان برای مثال با NET Core CLI. کار کرد. اما امکان اجرای این CLI به صورت زیر نیز وجود دارد:
docker run -it microsoft/dotnet:nanoserver dotnet --info
این دستور، مشخصات SDK نصب شده را نمایش می‌دهد و سپس مجددا به command prompt سیستم اصلی (که به آن میزبان، host و یا container host نیز گفته می‌شود) بازگشت داده خواهیم شد؛ چون کار NET Core CLI. خاتمه یافته‌است، پروسه‌ی متعلق به آن نیز خاتمه می‌یابد.
بدیهی است در این حالت تمام فایل‌های اجرایی داخل این container را نیز می‌توان اجرا کرد. برای مثال می‌توان کنسول پاورشل داخل این container را اجرا کرد:
docker run -it microsoft/dotnet:nanoserver powershell
زمانیکه به این کنسول دسترسی پیدا کردید، برای مثال دستور get-process را اجرا کنید. به این ترتیب می‌توانید لیست تمام پروسه‌هایی ر که هم اکنون داخل این container در حال اجرا هستند، مشاهده کنید.


هر کانتینر دارای یک File System ایزوله‌ی خاص خود است

تا اینجا دریافتیم که هر image، به همراه فایل‌های user space مورد نیاز خود نیز می‌باشد. به عبارتی هر image یک file system را نیز ارائه می‌دهد که تنها درون همان container قابل دسترسی می‌باشد و از مابقی سیستم جاری ایزوله شده‌است.
برای آزمایش آن، کنسول پاورشل را در سیستم میزبان (سیستم عامل اصلی که docker را اجرا می‌کند)، باز کرده و دستور \:ls c را صادر کنید. به این ترتیب می‌توانید لیست پوشه‌ها و فایل‌های موجود در درایو C میزبان را مشاهده نمائید. سپس دستور docker run -it microsoft/dotnet:nanoserver powershell را اجرا کنید تا به powershell داخل کانتینر NET Core. دسترسی پیدا کنیم. اکنون دستور \:ls c را مجددا اجرا کنید. خروجی آن کاملا متفاوت است نسبت به گزارشی که پیشتر بر روی سیستم میزبان تهیه کردیم؛ دقیقا مانند اینکه هارد درایو یک container متفاوت است با هارد درایو سیستم میزبان.


این تصویر زمانی تهیه شده‌است که دستور docker run یاد شده را صادر کرده‌ایم و درون powershell آن قرار داریم. همانطور که مشاهده می‌کنید یک Disk جدید، به ازای این Container در حال اجرا، به سیستم میزبان اضافه شده‌است. این Disk زمانیکه در powershell داخل container، دستور exit را صادر کنیم، بلافاصله محو می‌شود. چون پروسه‌ی container، به این ترتیب خاتمه یافته‌است.
اگر دستور docker run یاد شده را دو بار اجرا کنیم، دو Disk جدید ظاهر خواهند شد:


یک نکته: اگر بر روی این درایوهای مجازی کلیک راست کرده، گزینه‌ی change drive letter or path را انتخاب نموده و یک drive letter را به آن‌ها نسبت دهید، می‌توانید محتویات داخل آن‌ها را توسط Windows Explorer ویندوز میزبان نیز به صورت یک درایو جدید، مشاهده کنید.


خلاصه‌ای از ایزوله سازی‌های کانتینرها تا به اینجا

تا اینجا یک چنین ایزوله سازی‌هایی را بررسی کردیم:
- ایزوله سازی File System و وجود یک disk مجازی مجزا به ازای هر کانتینر در حال اجرا.

- پروسه‌های کانتینرها از پروسه‌های میزبان ایزوله هستند. برای مثال اگر دستور get-process را داخل یک container اجرا کنید، خروجی آن با خروجی اجرای این دستور بر روی سیستم میزبان یکی نیست. یعنی نمی‌توان از داخل کانتینرها، به پروسه‌های میزبان دسترسی داشت و دخل و تصرفی را در آن‌ها انجام داد که از لحاظ امنیتی بسیار مفید است. هر چند اگر به task manager ویندوز میزبان مراجعه کنید، می‌توان پروسه‌های داخل یک container را توسط Job Object ID یکسان آن‌ها تشخیص دهید (مثال آخر قسمت قبل)، اما یک container، قابلیت شمارش پروسه‌های خارج از مرز خود را ندارد.

- ایزوله سازی شبکه مانند کارت شبکه‌ی مجازی کانتینر IIS که در قسمت قبل بررسی کردیم. برای آزمایش آن دستور ipconfig را در داخل container و سپس در سیستم میزبان اجرا کنید. نتیجه‌ای را که مشاهده خواهید کرد، کاملا متفاوت است. یعنی network stack این دو کاملا از هم مجزا است. شبیه به اینکه به یک سیستم، چندین کارت شبکه را متصل کرده باشید. اینکار در اینجا با تعریف virtual network adaptors انجام می‌شود و لیست آن‌ها را در قسمت «All Control Panel Items\Network Connections» سیستم میزبان می‌توانید مشاهده کنید. یکی از مهم‌ترین مزایای آن این است که اگر در یک container، وب سروری را بر روی پورت 80 آن اجرا کنید، مهم نیست که در سیستم میزبان، یک IIS در حال سرویس دهی بر روی پورت 80 هم اکنون موجود است. این دو پورت با هم تداخل نمی‌کنند.

- در حالت کار با Windows Containers، رجیستری کانتینر نیز از میزبان آن مجزا است و یا متغیرهای محیطی این‌ها یکی نیست. برای مثال دستور \:ls env را در کانتینر و سیستم میزبان اجرا کنید تا environment variables را گزارش گیری کنید. خروجی این دو کاملا متفاوت است. برای مثال حداقل computer name، user name‌های قابل مشاهده‌ی در این گزارش‌ها، متفاوت است و یا دستور \:ls hkcu را در هر دو اجرا کنید تا خروجی رجیستری متعلق به کاربر جاری هر کدام را مشاهده کنید که در هر دو متفاوت است.

- در حالت کار با Linux Containers هر چیزی که ذیل عنوان namespace مطرح می‌شود مانند شبکه، PID، User، UTS، Mount و غیره شامل ایزوله سازی می‌شوند.


دو نوع Windows Containers وجود دارند

در ویندوز، Windows Server Containers و Hyper-V Containers وجود دارند. در این قسمت تمام کارهایی را که بر روی ویندوز سرور انجام دادیم، در حقیقت بر روی Windows Server Containers انجام شدند و تمام Containerهای ویندوزی را که در قسمت قبل بر روی ویندوز 10 ایجاد کردیم، از نوع Hyper-V Containers بودند.
تفاوت مهم این‌ها در مورد نحوه‌ی پیاده سازی ایزوله سازی آن‌ها است. در حالت Windows Server Containers، کار ایزوله سازی پروسه‌ها توسط کرنل اشتراکی بین کانتینرها صورت می‌گیرد اما در Hyper-V Containers، این ایزوله سازی توسط hypervisor آن انجام می‌شود؛ هرچند نسبت به ماشین‌های مجازی متداول بسیار سریع‌تر است، اما بحث به اشتراک گذاری کرنل هاست را که پیشتر در این قسمت بررسی کردیم، در این حالت شاهد نخواهیم بود. ویندوز سرور 2016 می‌تواند هر دوی این ایزوله سازی‌ها را پشتیبانی کند، اما ویندوز 10، فقط نوع Hyper-V را پشتیبانی می‌کند.


روش اجرای Hyper-V Containers بر روی ویندوز سرور

در صورت نیاز برای کار با Hyper-V Containers، نیاز است مانند قسمت قبل، ابتدا Hyper-V را بر روی ویندوز سرور، فعالسازی کرد:
Install-WindowsFeature hyper-v
Restart-Computer -Force
اکنون برای اجرای دستور docker run ای که توسط Hyper-V مدیریت می‌شود، می‌توان به صورت زیر، از سوئیچ isolation استفاده کرد:
docker run -it --isolation=hyperv microsoft/dotnet:nanoserver powershell
در این حالت اگر به disk management سیستم میزبان مراجعه کنید، دیگر حالت اضافه شدن disk مجازی را مشاهده نمی‌کنید. همچنین اگر به task manager ویندوز میزبان مراجعه کنید، دیگر لیست پروسه‌های داخل container را نیز در اینجا نمی‌بینید. علت آن روش ایزوله سازی متفاوت آن با Windows Server Containers است و بیشتر شبیه به ماشین‌های مجازی عمل می‌کند. در کل اگر نیاز به حداکثر و شدیدترین حالت ایزوله سازی را دارید، از این روش استفاده کنید.
اشتراک‌ها
آموزش Microservices و Docker در NET Core.

Book cover

این آموزش کاملترین آموزش ماکروسافت برای توسعه برنامه‌های مبتنی بر معماری Microservices و مدیریت آنها با استفاده از Docker Containers در NET Core. است. 

برای یادگیری بهتر می‌توانید پروژه eShopOnContainers   که بر اساس مفاهیم این آموزش نوشته شده است را بررسی کنید.

لینک دانلود کتاب به صورت PDF 

آموزش Microservices و Docker در NET Core.
نظرات اشتراک‌ها
زندگی پس از Google Reader؛ نگاهی به گزینه‌های مهیا
برنامه gReader تحت اندروید که قبلا فقط با گودر کار می‌کرد در بروز رسانی‌های جدید با Feedly کار می‌کنه و UI برنامه را هم ارتقا داده به نحوی که سرعت کار کردن با اون به وضوح افزایش یافته. امکانات اون هم بیشتر شده نسبت به نسخه‌های قبلی
نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 1 - NET Core. چیست؟
یک نکته‌ی تکمیلی: فعال سازی Dot net core 3 در Visual Studio 2019

وقتی Visual Studio 2019  را نصب میکنید به طور پیش فرض Core 2 را دارا هست اما نسخه dote net core 3 را ندارد. برای نصب dot net core 3 ابتدا  SDK مربوطه را دانلود کنید. تا این لحظه آخرین نسخه dotnet-sdk-3.0.100-preview7-012821 می‌باشد. ممکن است برای شما نسخه جدیدتری آمده باشد. سپس آن را نصب کنید و چون نسخه  preview هست باید در visual studio 2019 آن را فعال کنید. در نسخه‌های قدیمی‌تر ویژوال استدیو 2019 ابتدا از منوی Tools مسیر زیر را دنبال کنید:
Tools > Options > Project and Solutions > .Net Core
سپس تیک مربوط به Use preview of the .NET Core SDK را قرار دهید. چنانچه این گزینه را نداشتید از مسیر زیر استفاده کنید:
Tools -> Options -> Environment -> Preview Features
حال تیک مربوط به Use preview of the .NET Core SDK را بزنید.
اکنون میتوانید پروژه خود را اجرا کنید.
اشتراک‌ها
لیست پروژه های opensource شرکت facebook

پروژه هایی در زمینه هایی از قبیل Android، Artificial Intelligence، Data Infrastructure،

Developer Operations،Development Tools، Frontend ، iOS ،Languages ،Linux ،Security ،

Virtual Reality 

لیست پروژه های opensource شرکت facebook