مطالب
اتصال به سرویس WCF در NETCF 3.5

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

در این بین برای عقب نماندن از تکنولوژی‌های جدید بوجود آمده در حوزه دات نت مانند WCF این مقاله کمکی هر چند کوچک برای استفاده از این قابلیت موثر در فریمورک کامپکت می‌تواند باشد.

پیشنیاز‌های لازم:

- Microsoft Visual Studio 2008 + Service Pack 1

- نصب Power Toys for .NET Compact Framework 3.5


پیاده سازی سرویس (بر روی سیستمی غیر از ویندوز کامپکت):  

در ویژوال استودیو 2008 سرویس پک یک، پروژه ای از نوعclass library  را  ایجاد کرده و سرویسی تستی را برای استفاده ایجاد میکنیم:  
[ServiceContract(Namespace = "http://samples.wcf.cfnet.sample")]
    public interface ICalculator
    {
        [OperationContract]
        int Add(int a, int b);
    }

و پیاده سازی آن:

public class CalculatorService : ICalculator
    {
        public static int count;

        public int Add(int a, int b)
        {
            count++;
            Console.WriteLine(string.Format("{3}\tReceived 'Add({0}, {1})' returning {2}", a, b, a + b, count));
            return a + b;
        }

سرور سرویس:

برای هاست این سرویس از یک برنامه‌ی کنسول که در سلوشن ایجاد میکنیم استفاده میکنیم. البته امکان‌های دیگر برای هاست سرویس در هر پروسس دات نتی را میتوان یاد آور شد. برای هاست کردن شروع یک سرویس WCF باید یک IP درون شبکه را که قابل دسترسی از سمت ویندوز کامپکت بوده و به سیستم انتساب داده شده، دریافت و استفاده کنیم:  

var addressList = Dns.GetHostEntry(Dns.GetHostName());

string hostIP = addressList.AddressList.Single(x=>x.ToString().StartsWith("192.168.10.")).ToString();
Uri address = new Uri(string.Format("http://{0}:8000/Calculator", hostIP));

در قطعه بالا IP در رنج مناسب و قابل دسترسی انتخاب میشود چون ویندوز کامپکت (فارق از اینکه در شبیه ساز باشد یا واقعی) از طریق شبکه به سرور دسترسی پیدا میکند باید IP مناسب انتساب داده شده انتخاب شود.

ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService),address);
serviceHost.AddServiceEndpoint(typeof(ICalculator), new BasicHttpBinding(), "Calculator");

در ادامه یک سرویس هاست را new کرده و سرویس و بایندینگ را به آن در سازنده پاس میدهیم.

var serviceMetadataBehavior =
new ServiceMetadataBehavior { HttpGetEnabled = true };
serviceHost.Description.Behaviors.Add(serviceMetadataBehavior);

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

serviceHost.Open();
Console.WriteLine("CalculatorService is running at " + address.ToString());
Console.WriteLine("Press <ENTER> to terminate");
Console.ReadLine();
serviceHost.Close();

و در نهایت، شروع سرویس با فرمان Open و خاتمه آن با فرمان Close .


کلاینت سرویس (در داخل ویندوز کامپکت):

همراه با ارائه دات نت فریمورک 3.5 برای کار با سرویس WCF که از آن یک نسخه‌ی ارائه شده برای کامپکت نیز تهیه شده‌است، ابزاری مانند netcfSvcUtil.exe که در SDK نسخه‌ی کامپکت موجود است و کاربرد هندل کردن بعضی از موارد مانند تولید کد پروکسی‌های سمت کلاینت را دارد که در ادامه طرز استفاده از آن را بررسی خواهیم کرد. بعد از اجرای سرویس WCF با رفتار HttpGetEnabled = true برای بررسی سریع کارکرد صحیح سرویس، آدرس آن را در مرورگر میبینیم. تصویر زیر نتیجه‌ی آن در مرورگر است:



در خط فرمان به آدرس مربوط به این ابزار رفته (بسته به نسخه‌ی سیستم عامل ممکن است در پوشه‌های زیر یافت شود ( :

(Windows Drive)\Program Files (x86)\Microsoft.NET\SDK\CompactFramework\v3.5\bin
(Windows Drive)\Program Files\Microsoft.NET\SDK\CompactFramework\v3.5\bin

و فرمان زیر را اجرا میکنیم:

netcfSvcUtil.exe /language:C# /target:code /directory:D:\GeneratedCode\CF\CaculatorService http://192.168.10.189:8000/BooksService.svc?wsdl

البته ذکر IP شبکه در اینجا الزامی نیست؛ زیرا در صورت استفاده از آدرسهای داخلی سیستم، این فرمان به مشکلی بر نخواهد خورد. در این فرمان تولید کد با زبان c# و تولید کد که بصورت پیش فرض نیز وجود دارد و محل ذخیره سازی کدهای تولیدی را مشخص میکنیم و بعد از اجرای این فرمان، باید دو فایل در مسیر اشاره شده در فرمان تولید شود که اساس کار ما در سمت کلاینت خواهد بود:


کلاینت سرویس نیز با استفاده کدهای تولیدی بصورت زیر آماده سازی و اجرا میشود:

var addressList = Dns.GetHostEntry(Dns.GetHostName());
var localAddress = addressList.AddressList.Single(x => x.ToString().StartsWith("192.168.10.")).ToString();

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

var binding = CalculatorClient.CreateDefaultBinding();

نکته‌ای که دراین قسمت باید مدنظر قرار گیرد این است که در زمان تولید کدها اگر از localhost یا 127.0.0.1 و یا آدرسهای دیگر انتساب داده شده به سرور استفاده کرده باشید در متد CreateDefaultBinding از همان آدرس استفاده میشود و برای اصلاح آن بصورت زیر عمل میکنیم:

string remoteAddress = CalculatorClient.EndpointAddress.Uri.ToString();
remoteAddress = remoteAddress.Replace("localhost", serviceAddress.Text);

یک EndpointAddress با استفاده از این آدرس ساخته و به‌همراه بایندینگ، یک آبجکت از جنس CalculatorClient که در کدهای تولیدی داریم میسازیم:

CalculatorClient _client = new CalculatorClient(binding, endpoint);

برای تست نیز تنها متد این سرویس را با یک جفت عدد، صدا میزنیم:

var result = _client.Add(82, 18).ToString(CultureInfo.InvariantCulture);

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


 
مطالب
آشنایی با WebDav و نحوه استفاده از آن

WebDAV  استانداردی است بر روی پروتکل HTTP که Request‌ها و Responseهای مدیریت یک فایل را بر روی سرویس دهنده وب، تشریح می‌کند.

برای درک چرایی وجود این استاندارد بهتر است ذهن خود را معطوف به نحوه‌ی عملکرد سیستم فایل در OS کنیم که شامل API‌های خاص برای دسترسی نرم افزارهای گوناگون به فایل‌های روی یک سیستم است.
حال فکر کنید یک سرور Cloud راه اندازی نموده‌اید که قرار است مدیریت فایل‌ها و پرونده‌های Office را بر عهده داشته باشد و چون امکان ویرایش اسناد Office بر روی وب را ندارید، نیاز است تا اجازه دهید نرم افزار‌های Office مستقیما فایل‌ها را از روی سرور شما باز کنند و بعد از تغییرات، به جای ذخیره در سیستم local، محتوا را به فایل روی سرور ارسال کنند.
در مفهوم web عملا این کار غیر استاندارد و نادرست است. همه درخواست‌ها و جواب‌ها باید بر روی پروتکل Http باشند. خوب حال تصور کنید نرم افزارهای Office قابلیت آن را داشته باشند که به جای تحویل محتوا به سیستم عامل برای ذخیره‌ی آن بر روی سیستم local، محتوا را به یک آدرس ارسال نمایند و پشت آن آدرس، متدی باشد که بتواند به درخواست رسیده، به درستی پاسخ دهد.
این یعنی باید سمت سرور متدی با قابلیت ارسال پاسخ‌های درست و در سمت کلاینت نرم افزاری با قابلیت ارسال درخواست‌های مناسب وجود داشته باشد.
WebDAV استاندارد تشریح محتوای درخواست‌ها و پاسخ‌های مربوط به مدیریت فایل‌ها است.
خوشبختانه نرم افزارهای Office و بسیاری از نرم افزار‌های دیگر، استاندارد WebDAV را پشتیبانی می‌کنند و فقط لازم است برای سرورتان متدی با قابلیت پشتیبانی از درخواست‌های WebDAV پیاده سازی نمایید و البته متاسفانه کتابخانه‌های سورس باز چندانی برای WebDAV در سرور دات نت وجود ندارد. من ماژولی را برای کار با WebDAV نوشتم و سورسش را در Git  قرار دادم. برای این مثال هم از همین کتابخانه  استفاده می‌کنم.
ابتدا یک پروژه‌ی وب MVC ایجاد نمایید و پکیج xDav را از nugget نصب کنید.

PM> Install-Package xDav

اگر به web.config نگاهی بیاندازیم می‌بینیم یک module به نام xDav به وب سرور اضافه شده که بررسی درخواست‌های WebDAV  را به عهده دارد.

<system.webServer>
        <modules>
            <add name="XDav" type="XDav.XDavModule, XDav" />
        </modules>
  </system.webServer>

همچنین یک Section جدید هم به config برای پیکربندی xDav اضافه شده است. 

  <XDavConfig Name="xdav">
    <FileLocation URL="xdav" PathType="Local"></FileLocation>
  </XDavConfig>

خاصیت Name برای xDav نشانگر درخواست‌هایی است که باید توسط این ماژول اجرا شوند. در اینجا یعنی درخواست‌هایی که آدرس آن‌ها شامل "/xdav/" باشد، توسط این ماژول Handle می‌شوند. عبارت بعد از مقدار Name در URL هم طبیعتا نام فایل مورد نظر شماست.

FileLocation آدرس پوشه ای است که فایل‌ها در آن ذخیره و یا بازخوانی می‌شوند. اگر FileType با مقدار Local تنظیم شود، یعنی باید یک پوشه به نام خاصیت URL که در اینجا  xdav است در پوشه‌ی اصلی وب شما وجود داشته باشد و اگر با Server مقدار دهی شود URL باید یک آدرس فیزیکی بر روی سرور داشته باشد . مثل "c:\webdav"URL=

ما در این مثال مقادیر را به صورت پیش‌فرض نگه می‌داریم. یعنی باید در پوشه‌ی وب، یک Folder با نام xdav ایجاد کنیم.

در ادامه چند فایل word را برای تست در این پوشه کپی می‌کنم.

می خواهیم در صفحه Index، لیستی از فایل‌های درون این پوشه را نمایش دهیم طوری که در صورت کلیک بر روی هر کدام از آن‌ها، آدرس WebDav فایل مورد نظر را به Word ارسال کنیم.

بعد از نصب Office، در registry چند نوع Url تعریف می‌شود که معرف اپلیکیشنی است که آدرس به آن فرستاده شود. این دقیقا همان چیزیست که ما به آن نیاز داریم. کافیست آدرس WebDav فایل را بعد از عبارت " ms-word:ofe|u| " در یک لینک قرار دهیم تا آدرس به نرم افزار Word ارسال شود. یعنی آدرس URL باید این شکلی باشد:

  ms-word:ofe|u|http://Webaddress/xdav/filename

Webaddress آدرس وبسایت و filename نام فایل مورد نظرمان است. عبارت /xdav/ هم که نشان می‌دهد درخواست هایی که این آدرس را دارند باید توسط ماژول xDav پردازش شوند.

کلاسی با نام DavFile در پوشه‌ی Model ایجاد می‌کنم: 

public class DavFile
    {
        public string Name { get; set; }
        
        public string Href(string webAddress)
        {
            return string.Format("ms-word:ofe|u|http://{0}/xdav/{1}", webAddress, Name);
        }
    }

اکشن متد Index را در Home Controller، مانند زیر تغییر دهید:

var dir = new DirectoryInfo(XDav.Config.ConfigManager.DavPath);
            var model = dir.GetFiles().ToList()
                                      .Select(f => 
                                          new DavFile() { 
                                            Name = f.Name
                                        });
            return View(model);

یک لیست از فایل هایی که در پوشه‌ی webDav قرار دارند تهیه می‌کنیم و به View ارسال می‌کنیم. View را هم مثل زیر بازنویسی می‌کنیم. 

@model IEnumerable<WebDavServer.Models.DavFile>
<h1>
    File List
</h1>

<ul>
    @foreach (var item in Model)
    {
        <li> <a href="@Html.Raw(item.Href(ViewContext.HttpContext.Request.Url.Authority))">  @Html.Raw(item.Name) </a></li>
    }
</ul>

قرار است به ازای هر فایل، لینکی نمایش داده شود که با کلیک بر روی آن، آدرس فایل به word ارسال می‌شود. بعد از ثبت تغییرات، word محتوا را به همان آدرس ارسال می‌کند و ماژول xDav محتوا را در فایل فیزیکی سرور ذخیره خواهد کرد.

برنامه را اجرا کنید و بر روی فایل‌ها کلیک نمایید. اگر نرم افزار Office روی کامپیوترتان باز باشد با کلیک بر روی هر کدام از فایل‌ها، فایل word باز شده و می‌توایند محتوا را تغییر داده و ذخیره نمایید.

نرم افزار کلاینت (word) درخواست هایی با verb‌های مشخص که در استاندارد WebDav ذکر شده به آدرس مورد نظر می‌فرستد.  سرور WebDav درخواست را بر اساس Verb آن Request پردازش کرده و Response استاندارد را ایجاد میکند.

نرم افزار word پس از دریافت یک URL، به جای فرمت فیزیکی فایل، درخواست هایی را با تایپ‌های Option, Head, lock, get, post و unlock ارسال می‌کند. محتوای درخواست و پاسخ هر کدام از تایپ‌ها در استاندارد webDav تعریف شده و ماژول xDav آن را پیاده سازی نموده است.

  دریافت پروژه مثال

مطالب
راه اندازی Docker swarm
مقاله‌های زیادی درباره‌ی مزایای استفاده‌ی از داکر در اینترنت وجود دارند. در این مقاله قصد دارم طریقه‌ی راه اندازی یک سرور Production را برای داکر، توضیح دهم.
یکی از مزایای مهم داکر، امکان Scale در سریعترین زمان ممکن هست. یعنی اگر در محیط Production میزان بار بر روی یکی از اجزای محصول شما بیشتر بود (در صورتیکه معماری صحیحی برای سرویس‌های مجزا رعایت شده باشد)، می‌توانید آن قسمت را Scale کنید. می‌دانید که وجود بیشتر از یک Instance از یک سرویس، نگرانی‌های معمولی مثل Load Balancing  و غیره را به همراه دارد و اینکه کانتینر داکر شما قرار هست بر روی چه سروری نصب شود و بر روی چه سرور یا سرور‌هایی Scale شود.
ابزار‌های خوبی برای مدیریت کردن سرورهایی که Container‌های داکر بر روی آنها اجرا می‌شوند وجود دارند که یکی از مهمترین آنها kubernetes است که یکی از بهترین ابزارها، برای Management، Scaling و Deploy اتوماتیک نرم افزارهای Containerized می‌باشد.
در این مقاله از ابزاری به نام Swarm استفاده می‌کنم که راه اندازی آن راحت‌تر هست و همینطور وقتی احتیاج به Scaling وجود داشت، فقط یک PublishedPort به سرویس اختصاص داده می‌شود و  Load Balancing کاملا اتوماتیک انجام خواهد شد.
برای نصب Swarm، من دو سرور لینوکسی را آماده می‌کنم. یکی از سرور‌ها نقش Master را ایفا می‌کند و دیگری نقش Worker را (به راحتی می‌شود تعداد Worker‌ها را افزایش داد). 
از مزایای Swarm هم می‌شود به این نکته اشاره کرد که وقتی به سخت افزار بیشتری احتیاج باشد، کافی است یک ماشین دیگر را راه اندازی و آن را به عنوان یک Worker به Master معرفی کنیم و لازم نیست نگرانی درباره‌ی اینکه چه کانتینری بر روی چه سروری اجرا می‌شود داشته باشیم.
مشخصات سرور‌ها:
DocerMaster :
OS : CentOS7
IP: 192.168.64.3

DockerWorker:
OS: CentOS7
IP: 192.168.64.4
مطمئن شوید که هر دو در یک شبکه هستند و همدیگر را پینگ می‌کنند.
سپس بر روی هر دو سرور، داکر را نصب می‌کنم:
sudo yum install docker
و در ادامه سرویس داکر را  بر روی هر دو سیستم استارت می‌کنیم: 
$ sudo service docker start
$ sudo systemctl start docker.service
 مطمئن شوید که داکر در هر دو سرور نصب و اجرا شده‌است. با دستور service status docker می‌توانید نتیجه را ببینید.
بعد از نصب داکر، به صورت پیشفرض Swarm هم نصب می‌شود و لازم به نصب ابزار دیگری نیست.
برای ارتباط بین Master و Worker‌ها باید بعضی از پورت‌ها در این سرور‌ها باز شود. برای این کار در کامپیوتر Master دستورات زیر را اجرا کنید تا پورت‌ها به فایروال اضافه شوند: 
firewall-cmd --permanent --add-port=2376/tcp
firewall-cmd --permanent --add-port=2377/tcp
firewall-cmd --permanent --add-port=7946/tcp
firewall-cmd --permanent --add-port=7946/udp
firewall-cmd --permanent --add-port=4789/udp
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --reload
و سپس دستور زیر را اجرا کنید: 
systemctl restart docker
به کامپیوتر Worker رفته و با دستورات زیر پورت‌های لازم را به فایروال اضافه کنید: 
~]# firewall-cmd --permanent --add-port=2376/tcp
~]# firewall-cmd --permanent  --add-port=7946/tcp
~]# firewall-cmd --permanent --add-port=7946/udp
~]#  firewall-cmd --permanent --add-port=4789/udp
~]# firewall-cmd --permanent --add-port=80/tcp
~]#  firewall-cmd --reload
~]#  systemctl restart docker
در سرور Master باید Swarm را راه اندازی کنیم. برای این کار از دستور زیر استفاده می‌کنیم: 
sudo docker swarm init –advertise-addr 192.168.64.3
 بعد از اجرای موفقیت آمیز دستور فوق، عبارت زیر نمایش داده می‌شود: 

همانطور که مشاهده می‌کنید، پس از راه اندازی، اعلانی مبنی بر اینکه این نود به عنوان Manager شناخته شده و اینکه برای اضافه کردن یک نود Worker چه دستوری را باید اجرا کرد، نمایش داده شده‌است.

اکنون کافی‌است این خط کد را در نود Worker کپی کنیم: 

بعد از موفقیت آمیز بودن اجرای آن، می‌توانید در کامپیوتر Master، با دستور زیر تمام نود‌ها را مشاهده کنید: 

$ sudo docker node ls

همانطور که مشاهده می‌کنید، دو نود وجود دارد که یکی به عنوان Leader شناخته می‌شود. هر زمانی که نیاز بود، می‌شود به راحتی یک Worker دیگر را اضافه کرد.

برای راه اندازی یک کانتینر، swarm از CLI کاملی برخوردار هست؛ اما مایلم اینجا از یک ابزار خوب، برای مدیریت Swarm استفاده کنم. Portainer به عنوان یه ابزار عالی برای مدیریت Image‌ها و Container‌های داکر محسوب می‌شود که کاملا swarm را پشتیبانی می‌کند.

برای راه اندازی portainer کافی است کد زیر را در سیستم Master اجرا کنید: 

$ docker volume create portainer_data
$ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer

البته به دلیل عدم دسترسی به داکر هاب از کشور ایران، عملا امکان pull کردن این image، مستقیما از داکر هاب و بدون وی پی ان وجود ندارد. 

بعد از موفقیت آمیز بودن راه اندازی portainer می‌توانید از طریق آدرس http://192.168.64.3:9000 به آن دسترسی داشته باشید. در اولین ورود، پسورد ادمین را تنظیم می‌کنید و بعد از وارد شدن، صفحه‌ای مطابق شکل زیر را خواهید دید:  

اگر بر روی منوی swarm کلیک کنید، همه‌ی نود‌ها را مشاهده خواهید کرد و در صورتیکه بر روی Containers کلیک کنید، همه‌ی Container هایی را که بر روی این سرور وجود دارند، خواهید دید. مهمترین قسمت، بخش Service هاست که مشخصات Container هایی که روی swarm توزیع شدن را نشان می‌دهد و همینطور تعداد Container هایی از این image که Scale شدند. همینطور که می‌بینید فعلا فقط همین Portainer در حال اجراست. 


اجازه دهید یک مثال کاربردی‌تر بزنیم و یک سرویس را ایجاد کنیم.

من بر روی کامپیوتر شخصی‌ام و نه سرورها، با دستور زیر یک پروژه‌ی MVC را با دات نت Core ایجاد می‌کنم:

dotnet new mvc

و سپس دستور dotnet publish را اجرا می‌کنم و به پوشه‌ای که محتویات پابلیش شده در آن قرار دارند رفته و یک فایل بدون پسوند را به نام dockerfile ایجاد می‌کنم و متن زیر را در آن می‌نویسم:

همینطور که می‌بینید من از image مخصوص اجرای دات نت Core در این container استفاده می‌کنم. پوشه‌ی کانتینر را تنظیم می‌کنم و همه‌ی فایل‌هایی که در پوشه‌ی جاری سیستم خودم وجود دارند را به پوشه‌ی جاری کانتینر منتقل می‌کنم و سپس دستور دات نت را با پارامتر اسم dll پروژه‌ام اجرا می‌کنم. این کل محتویات فایل داکر من هست.

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

docker build –t swarmtest:dev .
با این دستور یک image از روی داکر فایلی که ایجاد کردیم درست می‌شود:

حالا باید این image را به سرور master منتقل کنیم. برای این کار راه‌های مختلفی وجود دارند که معمول‌ترین آن‌ها push کردن این image به یک registery و سپس pull کردن آن در کامپیوتر Master است که من از این راه استفاده نمی‌کنم. من این image را بر روی کامپیوترم ذخیره می‌کنم و سپس فایل ذخیره شده را به سرور master منتقل می‌کنم و آنجا آن فایل را load می‌کنم.
در ادامه بر روی کامپیوتر خودم دستور زیر را اجرا می‌کنم:
docker save swarmtest:dev –o swarmtest.tar

طبق شکل زیر یک فایل tar که حاوی image برنامه من هست، ایجاد شد:

حالا با دستور زیر این فایل رو به سرور Master منتقل می‌کنم:

scp –r swarmtest.tar root@192.168.64.3:/srv/images

همانطور که می‌بینید، فایل tar به پوشه‌ای که قبلا در سرور ایجاد کردم، منتقل شد.

حالا به سرور و پوشه‌ای که فایل tar آنجا قرار دارد رفته و با دستور زیر این image را بر روی سیستم load می‌کنم:

sudo docker load –i swarmtest.tar

همانطور که در تصویر می‌بینید، بعد از load شدن، image مورد نظرمان به داکر اضافه شده‌است.

حالا برای اجرا کردن این سرویس بر روی swarm، آدرس portainer را باز می‌کنیم و به قسمت  services می‌رویم و بر روی دکمه‌ی add service کلیک می‌کنیم:

در قسمت نام، نام سرویس و در قسمت imageConfiguration از منوی image‌ها، ایمیجی را که ایجاد کردیم، انتخاب می‌کنیم. در قسمت Replicas تعداد instance‌های container ای را که می‌خواهیم از روی image ایجاد شوند، مشخص می‌کنیم. (این قسمت را بر روی هر وضعیتی می‌توانیم قرار دهیم و زیاد و کم کنیم) و در قسمت port mapping، پورت درون Container و پورتی را که می‌خواهیم بر روی هاست به نمایش درآید، وارد می‌کنیم.

همانطور که می‌بینید من به راحتی می‌توانم تعداد Container‌ها را Scale کنم و نگرانی‌ای بابت load balancing و اینکه کدام container بر روی کدوم سرور ایجاد می‌شود، نخواهم داشت.

برای نمایش برنامه کافی است پورتی را که برای هاست وارد کردیم، با آی پی Master وارد کنیم:


مطالب
سیستم‌های توزیع شده در NET. - بخش اول - نیازمندی
در حوزه کاری ما همیشه نیازمندی‌های جدید باعث پیشرفت، ارتقاء و پیچیده‌تر شدن سیستم‌های سخت افزاری و نرم افزاری می‌شوند. بطور مثال زمانیکه نیاز شد چندین سیستم از داده‌های مشترکی استفاده کنند، در معماری Single Tier قسمت Database از سایر قسمت‌ها جدا شد و در سخت افزار دیگری قرار گرفت. به این صورت این معماری تبدیل به Two Tier شد و سپس برای اینکه تغییرات، کمترین تاثیر را در سیستم ما داشته باشد و با کمترین هزینه به Platform‌های دیگر نیز سرویس بدهیم، قسمت Presentation از سایر قسمت‌ها جدا شد و در سخت افزارهای دیگری قرار گرفت. به این صورت  تبدیل به معماری Three Tier شد و همینطور نیازهای جدید باعث شد معماری N Tier پیچیده‌تر شود. البته پیچیدگی که باعث تکامل آن شد یا بطور مثال نیاز به پردازش تعداد بیشتری عملیات بصورت همزمان، که باعث شد سیستم‌های ما از حالت Single Task تبدیل به Parallel System و سپس Distributed system شوند. واقعیت این است که در دنیای امروز، نیازهای جدیدی بوجود آمده‌اند؛ نیازهایی که یک سخت افزار به تنهایی قادر به ارائه راهکاری برای آنها نیست. واقعا یک سخت افزار که یک سرویس را ارائه می‌دهد، چه خصوصیاتی باید داشته باشد که مثلا در ثانیه حدود 50 میلیون عملیات را بصورت همزمان انجام دهد؛ یا مثلا سرویس مورد نظر حدود 400 میلیون کاربر فعال که روزانه بیشتر وقت خود را به استفاده از این سرویس اختصاص می‌دهند داشته باشد؟

آیا میدانستید که Facebook در حال حاضر بیشتر از 400 میلیون کاربر فعال دارد که حدود 200 میلیون از این کاربران هر روز از این سرویس استفاده می‌کنند؟ آماری که این سرویس داده به این صورت است که تا سال 2010، کاربرانش در هر روز حدود 16 بیلیون دقیقه از وقت خودشان را به استفاده از این سرویس اختصاص داده‌اند. در هر هفته کاربران این سرویس حدود 6 بیلیون مطلب را که شامل عکس و متن بوده را به اشتراک گذاشته‌اند. هر ماه بیشتر از 3 بیلیون عکس توسط این سرویس Upload شده‌است. کاربرانش روزانه بیشتر از 1 بیلیون عکس را توسط این سرویس مشاهده کرده‌اند. این سرویس در ثانیه حدود 50 میلیون عملیات را انجام می‌دهد!

آیا میدانستید که سرویس Twitter در حال حاضر 350 میلیون کاربر فعال دارد که حدود 100 میلیون از این کاربران روزانه از این سرویس استفاده می‌کنند و هر روز کاربران این سرویس 500 میلیون توییت را ارسال می‌کنند. این سرویس در فینال جام جهانی حدود 618,725 توییت را در یک دقیقه دریافت کرده‌است.
و یا سرویس Telegram حدود 100 میلیون کاربر فعال دارد که بصورت متوسط در هر روز 220 هزار کاربر جدید در آن ثبت نام می‌کنند. کاربران این سرویس روزانه 15 بیلیون پیام را ارسال می‌کنند و 700 میلیون عکس را به اشتراک می‌گذارند.
چه سروری به تنهایی می‌تواند این آمار و ارقام را پوشش دهد؟ هزینه خرید و نگهداری آن چقدر است؟ چقدر باید هزینه کنیم تا این سرور از دسترس خارج نشود؟ یک سرور به تنهایی چه راهکاری را می‌تواند ارائه دهد که هیچوقت از دسترس خارج نشود؟
بهتر است بدانید که سرویس Facebook روی بیش از 60,000 سرور ارائه می‌شود! چه سروری به تنهایی می‌تواند کارآیی 60,000 سرور را داشته باشد؟
چه نوع پایگاه داده ای که روی یک سرور سرویس ارائه می‌دهد، قادر است روزانه به بیشتر از 1 تریلیون درخواست، پاسخ دهد؟ اصلا چه سروری به تنهایی قادر است این حجم داده را که هر روز رو به افزایش است، نگهداری کند؟

بهتر است بدانید پایگاه داده سرویس‌های شرکت Apple، روی بیش از 75,000  Node قرار دارند که روزانه حدود 10 پتابایت داده ذخیره می‌کنند. یا Netflix که از 2,500  Node استفاده می‌کند و روزانه 420 ترابایت داده را در قالب 1 تریلیون درخواست دریافت می‌کند یا Easou که پایگاه داده آن روی 270  Node قرار دارد و روزانه 300 ترابایت داده را در قالب 800 میلیون درخواست دریافت می‌کند! اینها اعداد و ارقامی نیستند که ما بتوانیم با SqlServerی که روی یک سرور قرار دارد، پوشش دهیم.

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

در سری مقالات Distributed systems in .NET من سعی می‌کنم شما را با خصوصیات و اصطلاحات موجود در سیستمهای توزیع شده آشنا کنم و ابزارهایی را که در NET. برای پیاده سازی این نوع سیستم‌ها وجود دارد، به شما معرفی کنم و با هم ببینیم که این نوع سیستم‌ها چه راهکاری را برای رفع نیازمندی‌های ما ارائه می‌دهند.


نمونه‌ای از طراحی یک سیستم توزیع شده

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

آیا اینچنین مجموعه جامعی در مورد استراتژیهای طراحی GUI برنامه، ایجاد تعامل مفید بین کاربر و واسط کاربری و از این دست مطالب سراغ ندارید؟؟ (مسلما با توجه به نوع برنامه و سطح مخاطب آن، طراحی کاربر باید متفاوت باشد)

فکر میکنم لازم باشد که خودمان، اینچنین مجموعه‌ای از تصاویر واسطهای کاربری، در برنامه‌های موفق (فارسی) تهیه و در دسترس عموم قرار بدهیم.
مطالب
مدیریت AccessViolationException در برنامه‌های دات نت 4 به بعد
فرض کنید که از یک برنامه‌ی native ویندوز برای تهیه تصاویر سایت‌ها در یک برنامه‌ی وب استفاده می‌کنید و صبح که به سایت سر زده‌اید پیام در دسترس نبودن سایت قابل مشاهده است. مشکل از کجا است؟!

یک مثال ساده

using System;

namespace AccessViolationExceptionSample
{
    class Program
    {
        private static unsafe void AccessViolation()
        {
            byte b = *(byte*)(8762765876);
        }

        static void Main(string[] args)
        {
            try
            {
                AccessViolation();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }

            Console.WriteLine("Press a key...");
            Console.ReadKey();
        }
    }
}
برنامه‌ی کنسول فوق را پس از فعال سازی Allow unsafe code در قسمت تنظیمات پروژه، کامپایل کرده و سپس آن‌را خارج از VS.NET اجرا کنید. احتمالا انتظار دارید که قسمت catch این کد حداقل اجرا شود و سپس سطر «کلیدی را فشار دهید» ظاهر گردد. اما ... خیر! کل پروسه کرش کرده و هیچ پیام خطایی را دریافت نخواهید کرد. اگر به لاگ‌های ویندوز مراجعه کنید پیام زیر قابل مشاهده است:
 System.AccessViolationException.  Attempted to read or write protected memory.
 This is often an indication that other memory is corrupt.
و این نوع مسایل هنگام کار با کتابخانه‌های C و ++C زیاد ممکن است رخ دهند. نمونه‌ی آن استفاده از WebControl دات نت است یا هر برنامه‌ی native دیگری. در این حالت اگر برنامه‌ی شما یک برنامه‌ی وب باشد، عملا سایت از کار افتاده‌است. به عبارتی پروسه‌ی ویندوزی آن کرش کرده و بلافاصله از طرف ویندوز خاتمه یافته است.


چرا قسمت catch اجرا نشد؟

از دات نت 4 به بعد، زمانیکه دسترسی غیرمجازی به حافظه صورت گیرد، برای مثال دسترسی به یک pointer آزاد شده، استثنای حاصل، توسط برنامه catch نشده و اجازه داده می‌شود تا برنامه کلا کرش کند. به این نوع استثناءها Corrupted State Exceptions یا CSE گفته می‌شود. اگر نیاز به مدیریت آن‌ها توسط برنامه باشد، باید به یکی از دو طریق زیر عمل کرد:
الف) از ویژگی HandleProcessCorruptedStateExceptions بر روی متد فراخوان کتابخانه‌ی native باید استفاده شود. برای مثال در کدهای فوق خواهیم داشت:
   [HandleProcessCorruptedStateExceptions]
  static void Main(string[] args)
  {
ب) و یا فایل کانفیگ برنامه را ویرایش کرده و چند سطر ذیل را به آن اضافه کنید:
 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
     <legacyCorruptedStateExceptionsPolicy enabled="true" />
  </runtime>
</configuration>
در این حالت مدیریت اینگونه خطاها در کل برنامه همانند برنامه‌های تا دات نت 3.5 خواهد شد.
مطالب
به روز رسانی قالب فایل‌های csproj پروژه‌های قدیمی
اگر شما هم مثل بنده در حال نگه‌داری از یک سیستم قدیمی می‌باشید، به احتمال زیاد همیشه با یک سری مسائل مثل آپدیت پکیج‌ها، اضافه کردن یا حذف کردن فایلی از پروژه، مدیریت وابستگی‌ها، خروجی گرفتن از یک پروژه کنسول و ... درگیر هستید؛ مسائلی که سال‌هاست با روی کار آمدن «دات نت کور» و پس از آن «دات نت ۵» حل شده‌است. این حجم از مشکلات به حدی بود که گاهاً کدنویسی را با مشکل مواجه می‌کرد و امروز تصمیم گرفتم این مشکل را برای پروژه شرکت حل کنم. ابتدا باید بگویم، یک نسخه‌ی پشتیبان از کد خود تهیه کنید؛ چرا که زیاد به آن رجوع خواهید داشت.

انجام این آپدیت از طریق دو نرم‌افزار  upgrade-assistant و try-convert قابل انجام است. بنده به شخصه از upgrade-assistant استفاده کردم چرا که به نظر به بلوغ بیشتری رسیده‌است. ابتدا با دستور زیر این نرم‌افزار را نصب کنید:
dotnet tool install -g upgrade-assistant
اگر پیغامی دریافت میکنید مبتنی بر این که این نرم‌افزار قبلاً نصب شده، با دستور زیر از به‌روز بودن آن اطمینان حاصل کنید:
dotnet tool update -g upgrade-assistant

توجه داشته باشید، نرم‌افزار upgrade-assistant برای ارتقاء پروژه‌ها به دات نت 5 و 6 طراحی شده‌اند. بنابراین ما فقط با سه مرحله‌ی اول سر و کار داریم:
  • Back up project
  • Convert project file to SDK style
  • Clean up NuGet package references 

حال یک terminal را باز کنید و به محل پروژه بروید و دستور زیر را اجرا کنید:
upgrade-assistant upgrade MyProject.csproj
در دستور بالا، کلمه‌ی MyProject را با نام پروژه‌ی خود جایگزین کنید. پس از اجرای این دستور، مراحل ۱ تا ۳ را که پیش‌تر در مورد آن توضیح داده شد، اجرا کنید. توجه داشته باشید که اگر solution شما از بیش از یک پروژه تشکیل شده، برای هر پروژه به‌طور جداگانه‌ای باید این عمل تکرار شود. حال اگر فایل csproj پروژه را ببینید، متوجه آن می‌شوید که پروژه به ساختار جدید درآمده است.
  • اگر پروژه از جنس Library باشد، فایل csproj را به شکل زیر درآورید: 
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net472</TargetFramework>
        <OutputType>Library</OutputType>
    </PropertyGroup>
.
.
.
</Project>

  • اگر پروژه از جنس Console باشد، فایل csproj را به شکل زیر درآورید:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<OutputType>Exe</OutputType>
<ApplicationIcon>logo.ico</ApplicationIcon>
<AppConfig>App.config</AppConfig>
</PropertyGroup>
.
.
.
</Project>

  • اگر پروژه از جنس Web باشد، فایل csproj را به شکل زیر درآورید. احتمالاً پیچیده‌ترین و سخت‌ترین فایل‌ها، متعلق به پروژه‌های وب باشد. اگر دقت کنید نوع SDK از نوع MSBuild.SDK.SystemWeb می‌باشد. نسخه این SDK ممکن است در زمانیکه شما در حال خواندن این مطلب می‌باشد آپدیت شده باشد و بهتر است قبل از استفاده، آخرین نسخه را از نیوگت برداشت کنید. (باید ذکر کنم که این hack کوچک را از یک comment  در issueهای گیت‌هاب پیدا کردم.)
<Project Sdk="MSBuild.SDK.SystemWeb/4.0.54">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net472</TargetFramework>
<AppConfig>Web.config</AppConfig>
</PropertyGroup>
.
.
.
</Project>
مطالب
محاسبه مجدد میزان مصرف حافظه‌ی برنامه‌های دات نت

اگر به میزان مصرف حافظه‌ اولیه‌ی برنامه‌های دات نت دقت کنیم، نسبت به مثلا یک برنامه‌ی MFC چند برابر به نظر می‌رسند و ... این علت دارد:
زمانیکه یک برنامه‌ی مبتنی بر دات نت اجرا می‌شود، ابتدا JIT compiler شروع به کار کرده و شروع به کامپایل برنامه می‌کند. این بارگزاری هم در همان پروسه‌ی اصلی برنامه انجام می‌شود. به همین جهت میزان مصرف حافظه‌ی برنامه‌های دات نت عموما بالا به نظر می‌رسد.
اکنون سؤال اینجا است که آیا می توان این حافظه‌ای را که دیگر مورد استفاده نیست (و توسط JIT compiler اخذ شده) به سیستم بازگرداند و محاسبه‌ی مجددی را در این مورد انجام داد. پاسخ به این سؤال را در متد ReEvaluateWorkingSet زیر می‌توان مشاهده کرد:


using System;
using System.Diagnostics;

namespace Toolkit
{
public static class Memory
{
public static void ReEvaluateWorkingSet()
{
try
{
Process loProcess = Process.GetCurrentProcess();
//it doesn't matter what you set maxWorkingSet to
//setting it to any value apparently causes the working set to be re-evaluated and excess discarded
loProcess.MaxWorkingSet = (IntPtr)((int)loProcess.MaxWorkingSet + 1);
}
catch
{
//The above code requires Admin privileges.
//So it's important to trap exceptions in case you're running without admin rights.
}
}
}
}

در این متد ابتدا پروسه جاری دریافت شده و سپس MaxWorkingSet به یک عدد دلخواه تنظیم می‌شود. مهم نیست که این عدد چه چیزی باشد، زیرا این تنظیم سبب می‌شود که در پشت صحنه به شکل حساب شده‌ای حافظه‌ای که مورد استفاده نیست به سیستم بازگردانده شود و سپس عددی که در task manager نمایش داده می‌شود، مجددا محاسبه گردد. همچنین باید دقت داشت که این کد تنها با دسترسی مدیریتی قابل اجرا است و به همین دلیل وجود این try/catch ضروری است.

نحوه استفاده از متد ReEvaluateWorkingSet در برنامه‌های WinForms :
فایل Program.cs را یافته و سپس در روال رویداد گردان Idle برنامه، متد ReEvaluateWorkingSet را فراخوانی کنید (مثلا هر زمان که برنامه minimized شد اجرا می‌شود):

//Program.cs
namespace MemUsage
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//...

Application.Idle += applicationIdle;
}

static void applicationIdle(object sender, EventArgs e)
{
Memory.ReEvaluateWorkingSet();
}
}
}

نحوه استفاده از متد ReEvaluateWorkingSet در برنامه‌های WPF :
فایل App.xaml.cs را یافته و سپس در روال رویدادگردان Deactivated برنامه، متد ReEvaluateWorkingSet را فراخوانی کنید:

//App.xaml.cs

public App()
{
this.Deactivated += appDeactivated;
}

void appDeactivated(object sender, EventArgs e)
{
Memory.ReEvaluateWorkingSet();
}

تاثیر آن هم قابل ملاحظه است (حداقل از لحاظ روانی!). تست کنید!

مطالب
چرا باید از Git Hooks استفاده کنیم؟ معرفی Husky.Net
قبل از شروع این مقاله بهتر است ابتدا یک خاطره‌ی کوچک را تعریف کنم. مدتی پیش بود که برای سایت داکیومنتیشن یکی از پروژه‌های Open-Source سعی داشتم از vuepress که یک static site generator هست استفاده کنم. متاسفانه نسخه‌ی بتایی که استفاده میکردم یک فیچر مورد نیازم را نداشت و مجبور شدم خودم به‌آن این فیچر را اضافه کنم. سوروس را گرفتم، فیچر اضافه شد و ماجرا از اینجا شروع می‌شود ...

  • commit lint : اول اجازه نداد که کامیت را انجام دهم! با این توضیح که متن کامیت شما باید فرمت کامیت‌های این پروژه را رعایت کند. اگر به تصویر زیر دقت کنید، کامیت‌ها در این پروژه بسیار منظم هستند و هر کسی با هر متنی که دلش خواست کامیتی انجام نمی‌دهد. با یک بررسی کوچک متوجه شدم چطور باید فرمت این پروژه را رعایت کنم و مشکل رفع شد.



  • eslint : مرحله بعدی فایل‌هایی که تغییر داده بودم و یا ایجاد کرده بودم، با ابزار eslint به صورت خودکار بررسی شد. (eslint مشابه analyzer‌های دات نت است که برای بررسی مشکلات موجود در سورس کد استفاده می‌شود). در تصویر زیر یک مثال از خطای ایجاد شده‌ی توسط eslint را مشاهده می‌کنید. در این مرحله من با خطایی مواجه نشدم؛ ولی متوجه شدم کدی که نوشتم، مشکل خاصی ندارد. 



  • prettier  : مرحله سوم کد نوشته شده‌ی من توسط دستورات فرمتی که این پروژه رعایت می‌کند مانند style کد نویسی، استفاده از space یا tab، فاصله فرو رفتگی (indent) خطوط و غیره ... به صورت خودکار فرمت شد و کامیت من انجام شد. 

داشتن ابزاری که چنین کارهایی را به صورت خودکار انجام دهد، حتی اگر به تنهایی بر روی یک پروژه کار کنیم، بسیار مفید است و کیفیت کار و کد تولید شده را بالا می‌برد. ناگفته نماند که این موارد، یکی از اصلی‌ترین دغدغه‌های نگهداری پروژه است؛ بخصوص وقتی بیش از یک برنامه نویس داشته باشیم. به طور مثال قبل از اینکه من این امکانات را به پروژه‌های خودم اضافه کنم، با تیمی که کار میکردم، همیشه سعی داشتیم که از تنظیمات و حتی IDE مشترکی استفاده کنیم تا مثلا formatter کد ما مثل هم باشد. اگر این موارد قبل از هر کامیت انجام شوند، برنامه نویس‌ها میتوانند حتی در notepad کد نویسی انجام دهند و اطمینان داشته باشند، کد نوشته شده، هم اعتبار سنجی می‌شود و هم به صورت خودکار فرمت. 

تا اینجا با یکسری مزایای پایه‌ی داشتن چنین سیستمی آشنا شدیم ولی چطور این اتفاق رخ می‌دهد!؟ پاسخ: با استفاده از Git Hooks . البته ما صرفا محدود به انجام همین کارها نیستیم. هر کاری که در ترمینال سیستم قابل انجام باشد، از طریق Git hooks هم قابل انجام است. مثلا یکی دیگر از استفاده‌های رایج از git hooks میتواند اجرای Unit-Test‌ها قبل از push باشد.
در پروژه‌ی vuepress، تعداد زیادی ابزار به صورت هماهنگ نصب شده و کار میکنند که در نهایت با استفاده از Hook‌های Git، اجرا میشوند (مثلا از هوک pre-commit برای اجرای یکسری فرمان قبل از هر کامیت). با سه مورد از این ابزارها، در ابتدای این مقاله آشنا شدیم. ولی کلید اصلی تمام این‌ها، دو ابزار  husky  و  lint-staged هستند. husky ساخت و استفاده از git hook‌ها را آسان میکند و lint-staged وظیفه دارد تا فایل‌های stage شده‌ی در گیت را لیست کند و به سایر ابزارها برای اجرا ارسال نماید. 

شاید بپرسید که تمام اینها از Nodejs و npm استفاده میکنند، چطور میتوانیم از این امکانات در دات نت استفاده کنیم!   

معرفی Husky.Net 
این کتابخانه ترکیبی از ابزارهای husky و lint-staged و چند ابزار دیگر برای برنامه نویسان دات نت است. با استفاده از این ابزار به راحتی میتوانید از هوک‌های Git برای اجرای دستورات خود یا سایر ابزارها استفاده کنید. husky.net یک task-runner داخلی دارد که تعریف و ایجاد تسک‌های قابل اجرا توسط هوک‌ها را آسان میکند.

نصب husky.net
# local installation (recommended)
cd <Your project root directory>
dotnet new tool-manifest
dotnet tool install Husky

# global installation
dotnet tool install --global Husky

نکته نهایی
من در این مقاله قصد داشتم فقط دلیل نیاز به چنین ابزاری را شرح دهم؛ توضیح اینکه husky.net چه امکاناتی دارد و چطور میتوان از آن استفاده کرد، خارج از حوصله‌ی این مقاله است. اگر شد در آینده مقاله‌ای برای آموزش این ابزار تهیه میکنم؛ فعلا برای کسب اطلاعات بیشتر میتوانید به صفحه‌ی گیت هاب این ابزار مراجعه کنید و دلیل دیگر اینکه فکر میکنم الان زمان مناسبی برای آموزش نیست؛ چون این پروژه بسیار جوان است و به تازگی نوشته شده و احتمال اینکه فیچر‌های آن به زودی تغییر کند هم وجود دارد. همینطور از علاقمندان به مشارکت در پروژه‌های open-source دعوت می‌کنم که به بهبود این ابزار و یا تهیه‌ی ابزارهایی که در حال حاضر مشابهی در دات نت ندارند، کمک کنند. به طور مثال برای فرمت کد و آنالایزر، dotnet-format  مایکروسافت وجود دارد که بسیار کاربردیست؛ ولی ما ابزاری مثل commit lint هنوز در دات نت نداریم. فعلا در صورت نیاز باید برای استفاده از آن همچنان node و npm را نصب کنیم.