مطالب
Test Driven Development #3
در پست قبلی با  نوشتن یک تست ساده، با مفهوم TDD بیشتر آشنا شدیم .در این پست قصد بر این  است که به وسیله Mvc.Net شروع به نوشتن تست‌های جدی‌تر کرده و از مزایای آن بهره ببریم .
برای  شروع یک پروژه Mvc.Net ساخته و Nunit را در آن نصب می‌کنیم.
مدل زیر را در پوشه مدل‌ها می‌سازیم:
    public class Idea
    {
        public static List<Idea> Ideas = new List<Idea>
            {
                new Idea{Content="سایتی که در ایده به اشتراک گذاشته شود",Title = "سایت ایده ها"},
                new Idea{Content="عینک گوگل را فارسی کنم",Title = "عینک گوگل"},
            };
        public string Content { get;  set;}
        public string Title { get; set; }
    }
  [TestFixture]
    public class IdeaTest
    {
        [Test]
        public void ShouldDisplayListOfIdea()
        {
            var viewResult = new IdeaController().Index() as ViewResult;
            Assert.AreEqual(Idea.Ideas, viewResult.Model)
             Assert.IsNotNull(viewResult.Model);


        }    
 }

کد بالا شامل مقایسه مقدار خروجی Action با لیستی از مدل Idea و همچنین اطمینان از خالی نبودن مدل ارسالی به view می‌باشد.در خط اول یک وهله از Controller می سازیم و Action مورد نظر را به شی از جنس ViewResult  تبدیل(Cast) می‌کنیم پس از آن به وسیله viewResult.Model به مدلی که به سمت view پاس داده می‌شود دسترسی خواهیم داشت.اکنون اگر تست را اجرا  کنیم با خطای کامپایل مواجه می‌شویم.حال Controller و Action مورد نظر را به صورتی  که تست ما پاس شود پیاده سازی می‌کنیم.
    public class IdeaController : Controller
    {

        public ActionResult Index()
        {
            return View(Idea.Ideas);
        }
      }
کد بالا مقدار Ideas را به view برمیگرداند.
در این دروره ما به تست کردن ویو‌ها نخواهیم پرداخت.
تست بعدی تست ساده ای است که فقط میخواهیم از از وجود داشتن یک Action و نام view بازگشتی اطمینان حاصل کنیم.
   [Test]
        public void ShouldLoadCreateIdeaView()
        {
            var viewResult = new IdeaController().Create() as ViewResult;
            Assert.AreEqual(string.Empty, viewResult.ViewName);
        }
در کد بالا مثل تست قبل، یک وهله از Controller می سازیم و سپس نام view بازگشتی را با string.Empty مقایسه میکنیم به این معنی که view خروجی Action ما نباید نامی داشته باشد و براساس قرار دادها باید هم نام اکشن باشد.
حال نوبت به پیاده سازی اکشن رسید.:
        public ActionResult Create()
        {
            return View();
        }
در تست بعدی میخواهیم عملیات اضافه شدن یک Idea را به لیست بررسی کنیم:
    [Test]
        public void ShouldAddIdeaItem()
        {
            var idea = new Idea { Title = "شبکه اجتماعی ", Content = " شبکه اجتماعی سینمایی" };
            var redirectToRouteResult = new IdeaController().Create(idea) as RedirectToRouteResult;
            Assert.Contains(idea, Idea.Ideas);
            Assert.AreEqual("Index",redirectToRouteResult.RouteValues["action"]);
        }
تست بالا نیز مانند دو تست قبل است با این تفاوت که مخواهیم ریدارکت شدن به یک Action  خاص را نیز تست کنیم.برای همین مقدار خروجی را به RedirectToRouteResult تبدیل می‌کنیم.در ادامه یک Idea جدید ساخته و به لست اضافه میکنیم سپس از وجود داشتن آن در لیست Ideas اطمینان حاصل می‌کنیم.در خط آخر نیز نام Action که انتظار داریم بعد از اضافه شدن یک Idea ,کاربر به آن هدایت شود را ست می‌کنیم.
پیاده سازی Action  به شکل زیر است:
        public ActionResult Create(Idea idea)
        {
            Idea.Ideas.Add(idea);
            return RedirectToAction("Index");
        }

در این پست شما با مدل تست نویسی برایMvc.Net آشنا شدید.در مطلب بعدی شما با تست حذف و اصلاح Ideas آشنا خواهید شد. 
مطالب
ایجاد حالت‌های مختلف سایه با استفاده از CSS3
در CSS3 امکان ایجاد سایه توسط خصوصیت box-shadow ایجاد شده‌است که یکی از خصوصیت‌های محبوب در بین برنامه نویسان وب می‌باشد. در ادامه طریقه ایجاد چند نمونه از آن را توضیح خواهیم داد.
box-shadow به شما امکان تعریف چندین سایه را بر روی یک عنصر، با تعیین مقادیر رنگ، اندازه، میزان تیرگی (بلور) و میزان جابجایی (افست) آن، می‌دهد.
box-shadow: inset horizontal vertical blur spread color;
برای مثال:
box-shadow: 10px 10px;
box-shadow: 10px 10px 5px #888;
box-shadow: inset 2px 2px 2px 2px black;
box-shadow: 10px 10px #888, -10px -10px #f4f4f4, 0px 0px 5px 5px #cc6600;

Browser Support

می‌توان گفت تمام مرورگرها، خصوصیت box-shadow را پشتیبانی می‌کنند.
  • Internet Explorer 9.0 و بالاتر
  • Firefox 3.5 و بالاتر
  • Chrome 1.0 و بالاتر
  • Safari 3 و بالاتر
  • Opera 10.5 و بالاتر

برای استفاده از box-shadow در مرورگرهای نسخه پایین‌تر (بدون تغییر در نحوه تعریف)، باید از پیشوندهای هر مرورگر استفاده کنید. برای Firefox از moz-، برای Chrome و Safari از webkit- و Opera نیاز به پیشوند ندارد.

CSS مشترک برای افکت‌های زیر

.box {
     width:70%;
     height:200px;
     background:#FFF;
     margin:40px auto;
}

.box h3{
     text-align:center;
     position:relative;
     top:80px;
}

Effect 1

با انتساب افکت سایه استاندارد به این المنت، ظاهری شبیه به یک جعبه برجسته را پیدا می‌کند.

کد HTML

<div class="box effect1">
<h3>Effect 1</h3>
</div>

کد CSS

.effect1{
     box-shadow: 0 10px 6px -6px #777;
}

Effect 2

این افکت بر روی گوشه‌های المنت اثر خواهد گذاشت و ظاهر بلند کردن گوشه‌های جعبه را نمایش می‌دهد. با استفاده از دو خصوصیت before: و after: این افکت پیاده سازی می‌شود.

کد HTML

<div class="box effect2">
<h3>Effect 2</h3>
</div>

کد CSS

.effect2
{
  position: relative;
}
.effect2:before, .effect2:after
{
  z-index: -1;
  position: absolute;
  content: "";
  bottom: 15px;
  left: 10px;
  width: 50%;
  top: 80%;
  max-width:300px;
  background: #777;
  box-shadow: 0 15px 10px #777;
  transform: rotate(-3deg);
}
.effect2:after
{
  transform: rotate(3deg);
  right: 10px;
  left: auto;
}
توجه: برای افزایش میزان زاویه سایه با جعبه، مقدار transform را افزایش دهید. 
transform: rotate(8deg);

Effect 3

این افکت بر روی گوشه‌ی پایین چپ المنت اثر خواهد گذاشت و ظاهر بلند کردن گوشه جعبه را نمایش می‌دهد. با استفاده از خصوصیت before: این افکت پیاده سازی می‌شود.

کد HTML

<div class="box effect3">
<h3>Effect 3</h3>
</div>

کد CSS

.effect3
{
  position: relative;
}
.effect3:before
{
  z-index: -1;
  position: absolute;
  content: "";
  bottom: 15px;
  left: 10px;
  width: 50%;
  top: 80%;
  max-width:300px;
  background: #777;
  box-shadow: 0 15px 10px #777;
  transform: rotate(-3deg);
}

Effect 4

این افکت بر روی گوشه‌ی پایین راست المنت اثر خواهد گذاشت و ظاهر بلند کردن گوشه جعبه را نمایش می‌دهد. با استفاده از خصوصیت before: این افکت پیاده سازی می‌شود.

کد HTML

<div class="box effect4">
<h3>Effect 4</h3>
</div>

کد CSS

.effect4
{
  position: relative;
}
.effect4:after
{
  z-index: -1;
  position: absolute;
  content: "";
  bottom: 15px;
  right: 10px;
  left: auto;
  width: 50%;
  top: 80%;
  max-width:300px;
  background: #777;
  box-shadow: 0 15px 10px #777;
  transform: rotate(3deg);
}

Effect 5

این افکت یک سایه منحنی شکل را ایجاد می‌کند.

کد HTML

<div class="box effect5">
<h3>Effect 5</h3>
</div>

کد CSS

.effect5
{
    position:relative;       
    box-shadow:0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;
}
.effect5:before, .effect5:after
{
    content:"";
    position:absolute; 
    z-index:-1;
    box-shadow:0 0 20px rgba(0,0,0,0.8);
    top:50%;
    bottom:0;
    left:10px;
    right:10px;
    border-radius:100px / 10px;
}
توجه: برای اضافه کردن سایه به بالای جعبه، می‌توان تغییرات زیر را در افکت بالا ایجاد کرد.
.effect5:before, .effect5:after
{
    top:0;
} 
.effect5:after
{
    right:10px; 
    left:auto; 
    transform:skew(8deg) rotate(3deg);
}

Effect 6

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

کد HTML

<div class="box effect6">
<h3>Effect 6</h3>
</div>

کد CSS

.effect6
{
    position:relative;
    box-shadow:0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;
}
.effect6:before, .effect6:after
{
    content:"";
    position:absolute; 
    z-index:-1;
    box-shadow:0 0 20px rgba(0,0,0,0.8);
    top:10px;
    bottom:10px;
    left:0;
    right:0;
    border-radius:100px / 10px;
} 
.effect6:after
{
    right:10px; 
    left:auto; 
    transform:skew(8deg) rotate(3deg);
}

نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 14 - لایه بندی و تزریق وابستگی‌ها
- سطوح بالا و پایین در اینجا بر اساس رابطه‌ی  Presentation Layer-->BLL-->DAL-->Database مشخص می‌شوند (پایین‌ترین سطح بانک اطلاعاتی است و بالاترین سطح، همانی‌است که کاربر با آن تعامل دارد یا لایه‌ی نمایشی). در اینجا DAL بر اساس اینترفیس IUnitOfWork، امکان دسترسی به بانک اطلاعاتی را به صورت کنترل شده، در اختیار BLL (یا همان لایه سرویس در اینجا) قرار می‌دهد (مانند همان IUnitOfWork‌های تزریق شده‌ی در سازنده‌ی کلاس‌های لایه سرویس). سپس BLL هم منطق تجاری تعریف شده‌ی در آن‌را توسط اینترفیس‌هایی در اختیار بالاترین سطح سیستم که لایه نمایش هست (کنترلرها و قسمت MVC)  قرار خواهد داد. پیاده سازی‌های این‌ها هم توسط سیستم تزریق وابستگی‌ها تامین می‌شود و عموما اینترفیس‌های هر لایه هم در همان لایه تعریف می‌شوند و سپس در اختیار لایه‌های بالاتر قرار می‌گیرند. یعنی لایه‌های مختلف از جزئیات پیاده سازی‌های یک‌دیگر مطلع نیستند و فقط با اینترفیس‌ها کار می‌کنند که سهولت نوشتن آزمون‌های واحد را سبب خواهند شد و همچنین امکان تعویض ساده‌تر پیاده سازی‌ها در صورت نیاز، بدون نیاز به تغییرات جزئیات کدنویسی لایه‌ای خاص (اصل باز بودن برای توسعه و بسته بودن برای تغییر).
- همچنین از آنجائیکه ما در یک دنیای محض زندگی نمی‌کنیم، نیاز خواهید داشت از
IUnitOfWork گاهی از اوقات در لایه نمایشی هم استفاده کنید تا بتوانید یک تراکنش حاصل از چند موجودیت و چند کلاس لایه سرویس را در پایان کار درخواست رسیده (فقط یک تراکنش به ازای کل تعاملات یک درخواست)، به بانک اطلاعاتی اعمال کنید. بنابراین internal تعریف کردن آن در دنیای واقعی میسر نیست. حتی برای تعریف سیم‌کشی‌های تزریق وابستگی‌های اولیه‌ی آن هم نباید internal تعریف شود. همچنین باز هم یک برنامه‌ی واقعی الزاما تمام نیازهای تجاری‌اش در لایه سرویس قرار نمی‌گیرد. مثلا نیاز خواهید داشت با میان‌افزارها، اکشن فیلترها و غیره هم کار کنید که این‌ها محل قرارگیری استاندارد و مشخصی ندارند و هر جائی قابل تعریف هستند.
پروژه‌ها
Mvc File Manager
به نظر من بهترین روش برای یادگیری برنامه نویسی انجام  یک پروژه واقعی و کاربردی است . (هرچند ساده)  به همین دلیل در حین یادگیری asp.net mvc  تصمیم گرفتم یک فایل منجیر درست کنم که با پیشرفت در یادگیری asp.net mvc اون رو تکمیل کنیم.

فاز اول : نسخه 0.1.1       نسخه 0.1.2
پیاده سازی کنترلر های Browse,Download,Upload, CreateFolder,Delete
هدف از این مرحله یادگیری کنترلر ، ویو ، ویو مدل ، مدل بایندینگ ، روتینگ ، اکشن ریزالت و....
فاز دوم : (انجام شد) نسخه 0.2.3
پیاده سازی کنترلر های Rename,Properties 
استفاده از  WebGrid  در مرورگر فایل
محاسبه حجم پوشه
نسخه اصلاحی آقای وکیلی:دریافت
تغییر در نوع چینش models
افزودن PlUploader برای آپلود فایل
تغییر در Partial به نام _breadCrumb
امکان چند انتخاب هم زمان - و همچنین حذف  چند مورد هم زمان
باز شدن پوشه‌ها و دانلود شدن فایل‌ها با دابل کلیک 
تغییرات مختصر در style
تغییر در ساختار لینک برگشت -> انتقال از model به view و پیاده سازی با jquery به دلیل سهولت بیشتر

فاز سوم:
پیاده سازی اعتبار سنجی کاربران و نقش‌های آنان 
تعریف نقش‌های زیر (ایده خام)
Admin (Full access)
FileManager_Read(readonly access)
FileManager_Write(Creat Folder & upload file)
FileManager_Change(Move & Rename)
FileManager_Delete(Delete file and Folder
فاز چهارم :
پیاده سازی مراحل قبل تحت Ajax
فاز پنجم:
بهینه سازی و تکمیل پروژه
ایده‌های پراکنده :
قابلیت کپی 
قابلیت انتخاب چندتایی برای کپی و حذف و ...
 قابلیت آپلود همزمان چند فایل
چند زبانه بودن 
توسعه اینترفیس (درختواره پوشه‌ها ، ویوهای مختلف جهت نمایش فایل (لیست، آیکون‌های کوچک ، آیکون‌های بزرگ))
نمایش و تغییر دسترسی‌های ویندوز
آپلود فایل فشرده و اکسترکت کردن آن 
جستجوی فایل
.
.
.
نظرات مطالب
بارگزاری PartialView با استفاده از jQuery در زمان اجرا
نحوه صحیح ارسال پارامترها توسط jQuery Ajax
ارسال کوئری استرینگ‌ها به همراه عملیات Ajax در jQuery 

ضمنا بحث وب گرید، خارج از موضوع مطلب جاری است. از پارامتر ajaxUpdateContainerId آن افزونه خاص باید برای مدیریت مسایل paging و sorting استفاده کنید.
نظرات مطالب
کاهش تعداد بار تعریف using ها در C# 10.0 و NET 6.0.
یک نکته‌ی تکمیلی: ترفندی برای معرفی Stubs توسط ویژگی global using statements در unit tests

برای نمونه متد زیر را درنظر بگیرید:
public static string CurrentInvariantMonthName()
    {
        var month = DateTimeOffset.UtcNow.Month;
        return CultureInfo
            .InvariantCulture
            .DateTimeFormat
            .GetMonthName(month);
    }
در کل نوشتن آزمون واحد برای متدهایی که با زمان و خواصی مانند UtcNow و یا Now، کار می‌کنند، مشکل است. در آزمون‌های واحد نیاز داریم تا یک خروجی مشخص را با مقداری از پیش معلوم، مقایسه کنیم تا اطمینان حاصل شود که عملیات صورت گرفته، صحیح است. اما UtcNow هر بار متغیر است.
برای حل این مشکل، با استفاده از ویژگی global using statements و compiler directives، می‌توان دو مفهوم متفاوت را برای زمان ارائه داد:
#if MOCK_TIME
global using DateTimeOffset = StubDateTimeOffset; 
#else
global using DateTimeOffset = System.DateTimeOffset;
#endif

public static class StubDateTimeOffset
{
    private static System.DateTimeOffset? value;
    
    public static System.DateTimeOffset UtcNow 
        => value ?? System.DateTimeOffset.UtcNow ;
    
    public static void Set(System.DateTimeOffset dateTimeOffset) {
        value = dateTimeOffset;
    }

    public static void Reset() => value = null;
}
در اینجا یک فایل خالی cs. ایجاد شده و قطعه کد فوق در آن درج می‌شود. در این حالت اگر توسط تنظیمات کامپایلر در فایل csproj برنامه، MOCK_TIME فعال شود، مفهوم DateTimeOffset، دیگر همان System.DateTimeOffset استاندارد نخواهد بود؛ بلکه از یک کلاس بدلی به نام StubDateTimeOffset تامین می‌شود. برای فعالسازی MOCK_TIME هم می‌توان به صورت زیر عمل کرد که این تعریف، در فایل csproj پروژه‌ی آزمایشی قرار می‌گیرد:
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
   <DefineConstants>MOCK_TIME</DefineConstants>
</PropertyGroup>
پس از این تغییر، اینبار کلاس StubDateTimeOffset، تامین کننده‌ی DateTimeOffset در آزمون‌های واحد خواهد بود و در این آزمون‌ها می‌توان با مقدار دهی DateTimeOffset به صورت زیر، هربار آزمایشات را بر اساس یک UtcNow «مشخص» انجام داد:
// set time
StubDateTimeOffset.Set(new(new(2022, 7, 1)));
مطالب
مراحل ارسال یک پروژه‌ی Visual Studio به GitHub
از نگارش 2012 ویژوال استودیو، امکان کار با مخازن Git، به صورت یکپارچه و توکار و بدون نیاز به ابزارهای جانبی، توسط آن فراهم شده‌است. در ادامه قصد داریم به کمک این ویژگی توکار، نحوه‌ی ارسال یک پروژه‌ی از پیش موجود VS.NET را برای اولین بار به GitHub بررسی کنیم.


تنظیمات مقدماتی GitHub

در ابتدا نیاز است یک مخزن کد خالی را در GitHub ایجاد کنید. برای این منظور به برگه‌ی Repositories در اکانت GitHub خود مراجعه کرده و بر روی دکمه‌ی New کلیک کنید:


سپس در صفحه‌ی بعدی، نام پروژه را به همراه توضیحاتی وارد نمائید و بر روی دکمه‌ی Create repository کلیک کنید. در اینجا سایر گزینه‌ها را انتخاب نکنید. نیازی به انتخاب گزینه‌ی READ ME و یا انتخاب مجوز و غیره نیست. تمام این کارها را در سمت پروژه‌ی اصلی می‌توان انجام داد و یا VS.NET فایل‌های ignore را به صورت خودکار ایجاد می‌کند. در اینجا صرفا هدف، ایجاد یک مخزن کد خالی است.


از اطلاعات صفحه‌ی بعدی، تنها به آدرس مخصوص GitHub آن نیاز داریم. از این آدرس در VS.NET برای ارسال اطلاعات به سرور استفاده خواهیم کرد:



تنظیمات VS.NET برای ارسال پروژه به مخزن GitHub

پس از ایجاد یک مخزن کد خالی در GitHub، اکنون می‌توانیم پروژه‌ی خود را به آن ارسال کنیم. برای این منظور از منوی File، گزینه‌ی Add to source control را انتخاب کنید و در صفحه‌ی باز شده، گزینه‌ی Git را انتخاب نمائید:



سپس در کنار برگه‌ی Solution Explorer، برگه‌ی Team Explorer را انتخاب کنید. در اینجا بر روی دکمه‌ی Home در نوار ابزار آن کلیک کرده و سپس بر روی دکمه‌ی Unsynced commits کلیک نمائید.


در ادامه در صفحه‌ی باز شده، همان آدرس مخصوص مخزن کد جدید را در GitHub وارد کرده و بر روی دکمه‌ی Publish کلیک کنید:


در اینجا بلافاصله صفحه‌ی لاگینی ظاهر می‌شود که باید در آن مشخصات اکانت GitHub خود را وارد نمائید:


به این ترتیب عملیات Publish اولیه انجام شده و تصویر ذیل نمایان خواهد شد:


در اینجا بر روی دکمه‌ی Sync کلیک کنید. به این ترتیب مخزن کد GitHub به پروژه‌ی جاری متصل خواهد شد:


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


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


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



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



اعمال تغییرات بر روی پروژه‌ی محلی و ارسال به سرور

در ادامه می‌خواهیم دو فایل README.md و LICENSE.md را به پروژه اضافه کنیم. پس از افزودن آن‌ها، یا هر تغییر دیگری در پروژه، اینبار برای ارسال تغییرات به سرور، تنها کافی است به برگه‌ی Team explorer مراجعه کرده و ابتدا بر روی دکمه‌ی Home کلیک کرد تا منوی انتخاب گزینه‌‌های آن ظاهر شود. در اینجا تنها کافی است گزینه‌ی Changes را انتخاب و دقیقا همان مراحل عنوان شده‌ی پیشین را تکرار کرد. ابتدا ورود پیام Commit و سپس Commit. در ادامه Sync محلی و سپس Sync با سرور.
نظرات مطالب
یافتن لیست اسمبلی‌های ارجاعی
خیلی ممنون آقای نصیری ... ولی یه قضیه برام حل نشد . شما گفتید که: "کلاینت‌ها هر کدام نسخه‌ی کامل و لوکال خودشون رو باید داشته باشند (از طریق check out مخزن کد این پروژه لوکال باید تشکیل شود نه کپی دستی). سپس مثل اینکه دارند لوکال کار می‌کنند (نه از روی شبکه در حالت مپ شده). "
چجوری باید بدون اینکه اونا سرور رو مپ کنند و یا اینکه فایل ها رو خودشون کپی کنند به سورس ها دسترسی داشته باشند ؟

من الان دارم Viusal SVN Server رو دانلود میکنم , اما قبل این هم از Ankh-SVN-2.0.6347 استفاده میکردم که کار هم باهاش بسیار آسان بود ... اگر لطفی بکنید و در مورد ساخت نسخه مخصوص کلاینت ها یه توضیح بدید ممنون میشم .
مطالب
یک سرویس (میکروسرویس) چیست؟ و چگونه آن را مستند کنیم؟ (قسمت اول)
معماری میکروسرویس (یا به اختصار: میکروسرویس) یک سبک معماری نرم افزار می‌باشد که در آن یک نرم افزار، به مجموعه‌ای از سرویس‌ها خرد می‌شود؛ به نحوی که هر سرویس مسئولیت انجام بخشی از منطق کسب و کار را به عهده داشته باشد.
این تقسیم بندی مزایای متعددی را به همراه دارد که نهایتا پیاده سازی و توسعه راحت‌تر نرم افزار‌های بزرگ و پیچیده را ممکن می‌نماید. از جمله مزایای این معماری می‌توان به راحت‌تر شدن مباحث 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
مطالب دوره‌ها
استفاده از Async و Await در برنامه‌های ASP.NET MVC
از ASP.NET MVC 4 به بعد، امکان استفاده از اکشن متدهای async در ASP.NET MVC میسر شده‌است. البته همانطور که پیشتر نیز ذکر شد، شرط استفاده از امکانات async در نگارش‌های پیش از دات نت 4.5، استفاده از کامپایلری است که بتواند کدهای async را تولید کند و این مورد تنها از VS 2012 به بعد ممکن شده‌است.

علت استفاده از اکشن متدهای async در ASP.NET MVC

اگر نیاز دارید که برنامه‌ی وبی، به شدت مقیاس پذیر را تولید کنید، باید بتوانید مجموعه تردهای سیستم را تا حد ممکن مشغول به کار و سرویس دهی نگه دارید. در برنامه‌های وب ASP.NET تنها تعداد مشخصی ترد، برای پاسخ دهی به درخواست‌های رسیده، همواره مشغول به کار می‌باشند. در اینجا اگر این تردها را برای مدت زمان زیادی جهت اعمال IO مشغول نگه داریم، دست آخر به سیستمی خواهیم رسید که تردهای مفید آن، جهت پایان عملیات مرتبط بیکار شده‌اند و دیگر ASP.NET قادر نیست از آن‌ها جهت پاسخ‌دهی به سایر درخواست‌های رسیده استفاده کند.
برای مثال یک اکشن متد را درنظر بگیرید که نیاز است با یک وب سرویس، برای دریافت نتیجه کار کند. اگر این عملیات اندکی طول بکشد، به همین میزان ترد جاری درحال پردازش این درخواست، بیکار شده و منتظر دریافت پاسخ خواهد ایستاد و اگر به همین ترتیب تعداد تردهای بیکار، بیشتر و بیشتر شوند، دیگر سیستم قادر نخواهد بود به درخواست‌های جدید رسیده پاسخ دهد و ASP.NET مجبور خواهد شد این درخواست‌ها را در صف قرار دهد تا بالاخره زمانی این تردها آزاد شده و قابل استفاده‌ی مجدد گردند. برای رفع این مشکل، استفاده از اعمال غیرهمزمان ابداع گردیدند تا در آن‌ها ترد مورد استفاده جهت پردازش درخواست رسیده را آزاد کرده و به این ترتیب دیگر نیازی نباشد تا تردجاری، تا پایان عملیات IO بلاک شده و بدون استفاده باقی بماند.
در ASP.NET MVC 3 برای نوشتن اکشن متدهای async می‌بایستی از روش قدیمی مدل‌های Async در دات نت مانند APM استفاده می‌شد و همچنین کنترلر جاری بجای ارث بری از کلاس Controller می‌بایستی از کلاس AsyncController مشتق می‌شد. به علت سخت بودن استفاده از آن، این روش و کنترلرهای async در نگاش 3 آن آنچنان مقبولیت و استفاده‌ی گسترده‌ای نیافتند. چون هر اکشن متد تبدیل می‌شد به دو قسمت Begin و End متداول روش‌های APM. سپس در کشن متد دومی، نتیجه‌ی این عملیات به View بازگشت داده می‌شد.
از ASP.NET MVC 4 به بعد، خالی کردن تردهای سیستم و استفاده‌ی مجدد و مشغول به کار نگه داشتن مداوم آن‌ها با استفاده از امکانات توکار زبان‌هایی مانند سی‌شارپ 5، ساده‌تر و خواناتر شده‌است.
البته باید دقت داشت که این بحث صرفا سمت سرور بوده و ارتباطی به مباحث غیرهمزمان سمت کلاینت، مانند Ajax ندارد (A در Ajax نیز به معنای Async است) و از دید مصرف کننده‌ی نهایی، نامرئی می‌باشد. کار Aajx در سمت کلاینت نیز خالی کردن ترد UI مرورگر است (و نه سرور) و در نهایت تهیه‌ی برنامه‌هایی با قابلیت پاسخ‌دهی بهتر.


نوشتن اکشن متدهای Async در ASP.NET MVC

اولین کاری که باید صورت گیرد، اندکی تغییر امضای اکشن متدهای متداول است:
 public ActionResult Index()
این اکشن متد متداول، در یک ترد اجرا شده و این ترد تا پایان کار آن بلاک خواهد شد. برای مثال اگر قرار است مانند قسمت قبل، متد GetStringAsync در آن پردازش شود، تا پایان مدت زمان پردازش این متد، ترد جاری بلاک شده و سیستم قادر به استفاده‌ی مجدد از آن جهت پاسخ‌دهی به سایر درخواست‌های رسیده نخواهد بود. برای تبدیل آن به یک اکشن متد async باید به نحو ذیل عمل کرد:
 public async Task<ActionResult> Index()
ابتدا واژه‌ی کلیدی async به ابتدای امضای متد اضافه می‌شود. سپس خروجی آن اینبار بجای ActionResult، نسخه‌ی جنریک Task of T خواهد بود. همچنین دیگر نیازی نیست مانند MVC 3، کنترلر جاری از کلاس AsyncController مشتق شود.
زمانیکه به امضای متدی، async اضافه می‌شود، یعنی جایی در داخل بدنه‌ی آن باید await بکار رود:
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace Async11.Controllers
{
    public class HomeController : Controller
    {
        public async Task<ActionResult> Index()
        {
            var url = "https://www.dntips.ir";
            var client = new HttpClient(); // make sure you have an assembly reference to System.Net.Http.dll
            client.DefaultRequestHeaders.UserAgent.ParseAdd("Test Async");
            var result = await client.GetStringAsync(url);
            return View(result);
        }
    }
}
بنابراین اگر داخل اکشن متد جاری، جایی از await استفاده نمی‌شود، async کردن آن بی‌معنا است. این await است که سبب آزاد شدن ترد جاری جهت استفاده‌ی مجدد از آن برای پاسخ‌دهی به سایر درخواست‌های رسیده می‌شود.


یک نکته در مورد WCF 4.5

از WCF 4.5 به بعد، در صفحه‌ی معروف Add service references آن، با کلیک بر روی گزینه‌ی advanced و تنظیمات سرویس، امکان انتخاب گزینه‌ی Create task based operations نیز وجود دارد. این مورد دقیقا برای سهولت استفاده از آن با async و await سی‌شارپ 5 و مدل TAP آن طراحی شده‌است.


تعیین timeout در اکشن متدهای async

برای مشخص سازی صریح timeout در اکشن متدهای غیرهمزمان، می‌توان از ویژگی خاصی به نام AsyncTimeout به نحو ذیل استفاده کرد:
   [AsyncTimeout(duration: 1200)]
  public async Task<ActionResult> Index(CancellationToken ct)
در مورد لغو اعمال غیرهمزمان پیشتر صحبت شد. در اینجا پارامتر CancellationToken توسط فریم ورک جاری تنظیم شده و می‌توان آن‌را به متدهایی که قادرند اعمال غیر همزمان خود را بر اساس درخواست رسیده CancellationToken لغو کنند، ارسال کرد.


استفاده از قابلیت‌های غیرهمزمان EF 6 به همراه ASP.NET MVC 5

EF 6 به همراه یک سری متد و همچنین متد الحاقی جدید است که اعمال Async را پشتیبانی می‌کنند. اگر در حین انتخاب گزینه‌ی ایجاد کنترلر جدید، گزینه‌ی MVC 5 Controller with views, using EF را انتخاب کنید، امکان تولید اکشن متدهای async نیز به صورت پیش فرض پیش بینی شده‌است:


   public async Task<ActionResult> Index()
  {
     var model = await db.Books.ToListAsync();
     return View(model);
  }
در اینجا نیز امضای اکشن متد، همانند توضیحاتی است که در ابتدای بحث ارائه شد. فقط بجای متد ToList معمولی EF، از نگارش Async آن استفاده شده‌است و همچنین برای دریافت نتیجه‌ی آن از کلمه‌ی کلیدی await استفاده گردیده است.
به علاوه متد Find اکنون معادل FindAsync نیز دارد و همچنین SaveChanges دارای معادل غیرهمزمانی شده‌است به نام SaveChangesAsync .
البته باید دقت داشت که برای Where معادل Async ایی طراحی نشده‌است؛ زیرا نوع IQueryable صرفا یک عبارت است و اجرای آن تا زمانیکه ToList، First و امثال آن فراخوانی نشوند، به تعویق خواهد افتاد.