نظرات مطالب
Blazor 5x - قسمت 19 - کار با فرم‌ها - بخش 7 - نکات ویژه‌ی کار با EF-Core در برنامه‌های Blazor Server
در Blazor دیگر الگوی واحد کاری مطرح نیست؟ چرا که در کدهای داخل سرویس هم عملیات savechange در درون اتفاق می‌افتد. با توجه به تغییراتی که در dispose کردن سرویس‌ها داده میشود آیا این سرویس‌ها را میشود با یک پروژه نوع Api به اشتراک گذارد؟
مطالب
Best Practice هایی برای ASP.NET MVC - قسمت اول
در سایت جاری مطالب زیادی درباره ASP.NET MVC نوشته شده است. این مطلب و قسمت بعدی آن مروری خواهد داشت بر Best Practice‌ها در ASP.NET MVC.

استفاده از NuGet Package Manager برای مدیریت وابستگی‌ها

درباره اهمیت NuGet برای مصرف کنندگان قبلا این مطلب نوشته شده است.
بجای صرف وقت برای اینکه بررسی کنیم آیا این نسخه‌ی جدید کتابخانه‌ی X یا اسکریپت jQuery آمده است یا خیر، می‌توان این وظیفه را به NuGet سپرد. علاوه بر این NuGet مزیت‌های دیگری هم دارد؛ مثلا تیم‌های برنامه نویسی می‌توانند کتاب خانه‌های مشترک خودشان را در مخزن‌های سفارشی NuGet قرار دهند و توزیع و Versioning آن‌را به NuGet بسپارند.


تکیه بر Abstraction (انتزاع)

Abstraction در طراحی سیستم‌ها منجر به تولید نرم افزار هایی Loosely coupled با قابلیت نگهداری بالا و همچنین فراهم شدن زمینه برای نوشتن Unit Test می‌شود.
اگر به مطالب قبلی وب سایت برگردیم در مطلب چرا ASP.NET MVC گفته شد که :
2) دستیابی به کنترل بیشتر بر روی اجزای فریم ورک :
در طراحی ASP.NET MVC همه‌جا interface‌ها قابل مشاهد هستند. همین مساله به معنای افزونه پذیری اکثر قطعات تشکیل دهنده ASP.NET MVC است؛ برخلاف ASP.NET web forms. برای مثال تابحال چندین view engine، routing engine و غیره توسط برنامه نویس‌های مستقل برای ASP.NET MVC طراحی شده‌اند که هیچکدام با ASP.NET web forms میسر نیست. برای مثال از view engine پیش فرض آن خوشتان نمی‌آید؟
عوضش کنید! سیستم اعتبار سنجی توکار آن‌را دوست ندارید؟ آن‌را با یک نمونه بهتر تعویض کنید و الی آخر ...
به علاوه طراحی بر اساس interface‌ها یک مزیت دیگر را هم به همراه دارد و آن هم ساده سازی
mocking (تقلید) آن‌ها است جهت ساده سازی نوشتن آزمون‌های واحد.


از کلمه‌ی کلیدی New استفاده نکنید

هر جا ممکن است کار وهله سازی اشیاء را به لایه و حتی Framework دیگری بسپارید. هر زمان اشیاء نرم افزار خودتان را با کلمه‌ی new وهله سازی می‌کنید اصل Abstraction را فراموش کرده اید. هر زمان اشیاء نرم افزار را مستقیم وهله سازی می‌کنید در نظر داشته باشید می‌توانید آنها را به صورت وابستگی تزریق کنید.
در همین رابطه مطالب زیر را از دست ندهید :


از HttpContext مستقیما استفاده نکنید (از HttpContextBase استفاده کنید)

از .NET 4 به بعد فضای نامی تعریف شده که در بر دارنده‌ی کلاس‌های انتزاعی (Abstraction) خیلی از قسمت‌های اصلی ASP.NET است.  یکی از مواردی که در توسعه‌ی ASP.NET معمولا زیاد استفاده می‌شود، شیء HttpContext است . استفاده از HttpContextBase را به استفاده از HttpContext ترجیح دهید. اهمیت این موضوع در راستای اهمیت انتزاع (Abstraction) می‌باشد.


از "رشته‌های جادویی" اجتناب کنید

استفاده از رشته‌های جادویی در خیلی از جاها کار را ساده می‌کند؛ بعضی وقت‌ها هم به آنها نیاز است اما مشکلات زیادی دارند :
  1. رشته‌ها معنای باطنی ندارند (مثلا : دشوار است که از روی نام یک ID مشخص کنم این ID چطور به ID دیگری مرتبط است و یا اصلا ربط دارد یا خیر)
  2. با اشتباهات املایی یا عدم رعایت حروف بزرگ و کوچک ایجاد مشکل می‌کنند.
  3. به Refactoring واکنش خوبی نشان نمی‌دهند. (برای درک بهتر این مطلب را بخوانید.)
برای درک بهتر 2، یک مثال بررسی می‌شود؛ اولی از رشته‌های جادویی برای دسترسی به داده در ViewData استفاده می‌کند و دومی refactor شده‌ی مثال اول است که از strongly type مدل برای دسترسی به همان داده استفاده می‌کند.
<p>
    <label for="FirstName">First Name:</label>
    <span id="FirstName">@ViewData["FirstName"]</span>
</p>
مثال دوم :
<p>
    <label for="FirstName">First Name:</label>
    <span id="FirstName">@Model.FirstName</span>
</p>
مثلا مثال دوم مشکلات رشته‌های جادویی را ندارد.
در رابطه با Magic strings این مطلب را مطالعه بفرمایید.


از نوشتن HTML در کدهای "Backend" خودداری کنید

با توجه به اصل جداسازی وابستگی‌ها (Separation of Concerns) وظیفه‌ی کنترلر‌ها و دیگر کدهای backend رندر کردن HTML نیست. (ساده سازی کنترلر ها) البته در نظر داشته باشید که قطعا تولید HTML در متد‌های کمکی کلاس هایی که "تنها" وظیفه‌ی آنها کمک به View‌ها جهت تولید کد هست ایرادی ندارد. این کلاس‌ها بخشی از View در نظر گرفته می‌شوند نه کدهای "backend".


در View‌ها "Business logic" انجام ندهید

معکوس بند قبلی هم کاملا صدق می‌کند ، منظور این است که View‌ها تا جایی که ممکن است باید حاوی کمترین Business logic ممکن باشند. در واقع تمرکز View‌ها باید استفاده و نحوه‌ی نمایش داده ای که برای آن‌ها فراهم شده باشد نه انجام عملیات روی آن.


استفاده از Presentation Model را به استفاده مستقیم از Business Object‌ها ترجیح دهید

در مطالب مختلف وب سایت اشاره به اهمین ViewModel‌ها شده است. برای اطلاعات بیشتر بند ج آموزش 11 از سری آموزش‌های ASP.NET MVC را مطالعه بفرمایید.


If‌های شرطی را در View‌ها را در متد‌های کمکی کپسوله کنید

استفاده از شرط‌ها در View کار توسعه دهنده را برای یک سری اعمال ساده می‌کند اما می‌تواند باعث کمی کثیف کاری هم شود. مثلا:
@if(Model.IsAnonymousUser) {
    <img src="@Url.Content("~/content/images/anonymous.jpg")" />
} else if(Model.IsAdministrator) {
    <img src="@Url.Content("~/content/images/administrator.jpg")" />
} else if(Model.Membership == Membership.Standard) {
    <img src="@Url.Content("~/content/images/member.jpg")" />
} else if(Model.Membership == Membership.Preferred) {
    <img src="@Url.Content("~/content/images/preferred_member.jpg")" />
}
می‌توان این کد که تا حدودی شامل منطق تجاری هم هست را در یک متد کمکی کپسوله کرد :
public static string UserAvatar(this HtmlHelper<User> helper)
{
    var user = helper.ViewData.Model;
    string avatarFilename = "anonymous.jpg";
    if (user.IsAnonymousUser)
    {
        avatarFilename = "anonymous.jpg";
    }
    else if (user.IsAdministrator)
    {
        avatarFilename = "administrator.jpg";
    }
    else if (user.Membership == Membership.Standard)
    {
        avatarFilename = "member.jpg";
    }
    else if (user.Membership == Membership.Preferred)
    {
        avatarFilename = "preferred_member.jpg";
    }
    var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
    var contentPath = string.Format("~/content/images/{0}", avatarFilename)
    string imageUrl = urlHelper.Content(contentPath);
    return string.Format("<img src='{0}' />", imageUrl);
}
اکنون برای نمایش آواتار کاربر به سادگی می‌توان  نوشت :
@Html.UserAvatar()
به این ترتیب کد ما تمیز‌تر شده ، قابلیت نگهداری آن بالاتر رفته ، منطق تجاری یک بار و در یک قسمت نوشته شده از این کد در جاهای مختلف سایت می‌توان استفاده کرد و اگر لازم به تغییر باشد با تغییر در یک قسمت همه جا اعمال می‌شود.

منتظر قسمت بعدی باشید.
 
اشتراک‌ها
اعتبارسنجی IOptions توسط کتابخانه MiniValidation

In this post I described the problem that by default, DataAnnotation validation doesn't recursively inspect all properties in an object for DataAnnotation attributes. There are several solutions to this problem, but in this post I used the MiniValidation library from Damian Edwards. This simple library provides a convenience wrapper around DataAnnotation validation, as well as providing features like recursive validation. Finally I showed how you can replace the built-in DataAnnotation validation with a MiniValidation-based validator

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOptions<MySettings>()
    .BindConfiguration("MySettings")
    .ValidateMiniValidation() // 👈 Replace with mini validation
    .ValidateOnStart();

var app = builder.Build();
OptionsValidationException: 
  DataAnnotation validation failed for 'MySettings' member: 'Nested.Value' with errors: 'The Value field is required.'.; 
  DataAnnotation validation failed for 'MySettings' member: 'Nested.Count' with errors: 'The field Count must be between 1 and 100.'.
Microsoft.Extensions.Options.OptionsFactory<TOptions>.Create(string name)
Microsoft.Extensions.Options.OptionsMonitor<TOptions>+<>c__DisplayClass10_0.<Get>b__0()


اعتبارسنجی IOptions  توسط کتابخانه MiniValidation
مطالب
Best practiceهای یک پروژه Blazor
Blazor، چارچوبی است ارائه شده توسط مایکروسافت که به ما اجازه می‌دهد برنامه‌های تعاملی وب را با CSharp و بدون JavaScript بنویسیم. ‌Blazor از اساس، Component based بوده و در برنامه‌هایی که Backend نیز با CSharp نوشته شده باشد ( مثلا با ASP.NET Core) امکان به اشتراک گذاری کد بین کلاینت و سرور را نیز فراهم می‌کند. معماری Blazor معماری‌ای مدرن است که در دل خود از امکانات CSharp نیز به خوبی بهره برده است تا بتوان پروژه را به‌صورتی که قابلیت نگهداری بالایی داشته باشد، توسعه داد. Blazor در ذات معماری خود امکان نوشتن برنامه‌های Native موبایل را نیز می‌دهد و برای اثبات این مهم چندین دمو برای Proof of concept ارائه شده است؛ اما تا به امروز امکانی که به صورت Production ready باشد، ارائه نشده است.
Blazor به دو شکل کار می‌کند. Blazor Server و Blazor Client
در Blazor Client با استفاده از پشتیبانی نزدیک به 90% ای مرورگرها از Web Assembly که اجازه اجرای کدهای غیر JavaScript ای را در مرورگر می‌دهد، ابتدا DLLها به همراه HTML-CSS و عکس‌ها و ... دانلود شده و برنامه به صورت Single Page App کار می‌کند. در این مدل شما می‌توانید از تکنیک Pre rendering یا SSR نیز استفاده کنید تا تجربه کاربری بهتری را نیز ارائه دهید و یا در بحث SEO بهتر عمل کنید.
‌Blazor در سمت کلاینت از NET Standard 2.1 پشتیبانی می‌کند که عملا به شما اجازه می‌دهد بازه‌ی گسترده‌ای از Nuget Packageها را استفاده کنید.

البته Blazor Client به صورت Preview ارائه شده است و "فعلا" دو مشکل را دارد:
۱- امکان دیباگ خوبی ندارد.
۲- در عمل باید فایل‌های اجرایی برنامه به صورت wasm یا web assembly دانلود شوند که در این حالت سرعتی بسیار بالا و بیش از جاوااسکریپت خواهند داشت؛ اما تا این لحظه این فایلها به صورت DLL دانلود میشوند و در سمت مرورگر "تفسیر" میشوند که باعث میشود سرعت گاه تا 70 برابر کمتر شود.
البته این دو مشکل در زمان ارائه نسخه NET 5. حل خواهند شد و در پروژه نسخه نهایی خود این دو مشکل را نخواهد داشت.

Blazor مدل اجرای دومی نیز دارد که به آن Blazor Server نیز می‌گوییم. در این مدل کدها تماما سمت سرور اجرا می‌شوند و تعاملات UI به صورت Web Socket به کلاینت منتقل و یا از آن دریافت می‌شوند که در این مدل، امکان Debug بدون کوچک‌ترین مشکلی کار می‌کند و مشکلی از بابت کندی دانلود و اجرای DLLها ندارد. فقط چون کدها تماما سمت سرور اجرا می‌شوند، در زمانیکه ارتباط شبکه‌ای، مناسب نباشد، می‌توانیم در سناریوهای مختلف، شاهد لگ و کندی باشیم.

آینده‌ی Blazor در مدل اول آن تعریف شده‌است و در گذر زمان برای مدل Blazor Server، نمیتوان آینده‌ی زیادی را متصور بود. اما نکته‌ی مهم و جالب اینجاست که کد پروژه در هر دو مدل یکی است و فقط Configuration این دو از هم متفاوت است. پس اگر علاقمند به شروع به استفاده از Blazor هستید، یک راه این است که با مدل Blazor Server شروع و بعدا با تغییر Configuration، به Blazor Client سوئیچ کنید. البته در این میان باید به نکاتی دقت کنید:
۱- Blazor Server از NET Core 3.1. پشتیبانی می‌کند و Blazor Client از NET Standard 2.1. در مواقعی خاص ممکن است یک کلاس یا یک متد در NET Core 3.1. باشد و در NET Standard 2.1. نباشد.
۲- در Blazor Server شما حتی می‌توانید با Entity Framework Core مثلا به Sql Server وصل شوید؛ ولی طبیعتا Browser امکان اتصال مستقیم به Sql Server را در مدل Client به شما نخواهد داد.
این موارد باعث می‌شود که اگر پروژه را با Blazor Server شروع کنید، شاید در آن کارهایی را انجام دهید که در BlazorServer کار می‌کنند، ولی در BlazorClient کار نمی‌کنند و این باعث شود بعدا که نسخه نهایی و کامل BlazorClient آماده شد، شاید نتوانید به آن، به صورت ساده و آسانی سوئیچ کنید.

ایده آل این است که پروژه‌ای داشته باشید که به سادگی عوض کردن یک کلمه، بین این دو مدل سوئیچ کنید و بر صحت عملکرد پروژه در هر دو حالت نظارت داشته باشید. برای رسیدن به این مهم، پروژه‌ای را ساخته‌ام به نام BlazorDualMode که عمده‌ی بررسی‌ها را به صورت اتوماتیک انجام می‌دهد و عملا تضمینی بر مهاجرت آسان شما از BlazorServer به BlazorClient در آینده خواهد بود
اگر در نت جستجو کنید، پروژه‌هایی با این اسم را خواهید دید؛ اما این پروژه دو مزیت مهم را دارد که الباقی فاقد آن هستند:
۱- مدل Blazor Client آن دارای تکنیک Pre rendering یا SSR است.
۲- پروژه Api آن، از پروژه‌ی Blazor آن جداست. اگر پروژه‌ی Api را پروژه‌ای بدانیم که به همراه Model‌، Data و ... امکان دسترسی به DbContext و دیتابیس را دارد، جدا بودن پروژه‌ی Blazor باعث می‌شود که حتی در مدل Server آن نیز مجبور باشیم دیتا را از Api به صورت Rest Api call بگیریم و به واسطه پروژه Shared مابین Api و Blazor نهایت Dto‌ها و سایر کدهای مشترک را ببینیم.
البته در پروژه DualMode فقط پروژه Api درست شده‌است و ما کاری با جزئیات آن، اینکه مثلا CQRS می‌خواهیم یا نه، آیا میخواهیم لایه‌ای کار کنیم و ... نداریم و فقط روی موارد مرتبط با Blazor متمرکز شده‌ایم.
در پروژه یک فایل داریم به نام Directory.build.props. زمانیکه چنین فایلی در فولدری قرار بگیرد، تمامی موارد نوشته شده در آن به صورت ضمنی در تمامی فایل‌های csproj زیر مجموعه آن اعمال می‌شود.
در این فایل داریم:
<Project>
  <PropertyGroup>
    <BlazorMode>Client</BlazorMode>
    <DefineConstants Condition=" '$(BlazorMode)' == 'Client' ">$(DefineConstants);BlazorClient</DefineConstants>
    <DefineConstants Condition=" '$(BlazorMode)' == 'Server' ">$(DefineConstants);BlazorServer</DefineConstants>
    <LangVersion>8.0</LangVersion>
  </PropertyGroup>
</Project>
ابتدا یک متغیر را تعریف کرده‌ایم، به نام BlazorMode که می‌تواند یا Server باشدو یا Client، که فعلا Client است و به راحتی می‌توانید آن‌را عوض کنید.
در ادامه Define Constant کرده‌ایم که اجازه می‌دهد کدهای CSharp دخیل در Configuration پروژه Blazor را شرطی کنیم. برای مثال بنویسیم:
#if BlazorClient
...
#elif BlazorServer
...
#endif
این if‌ها که با # شروع می‌شوند، در هنگام Compile چک می‌شوند و کدی که در شرط با نتیجه true قرار بگیرد، Compile میشود و در خروجی برنامه قرار می‌گیرد و کدی که در شرط با نتیجه false قرار بگیرد، از اساس Compile نمیشود.
نسخه CSharp تمامی پروژه‌ها نیز 8.0 قرار داده شده است.

پروژه‌ی BlazorDualMode.Api آن پروژه‌ای است که Api Controllerها در آن قرار می‌گیرند و همچنین در حالت Blazor Client، پروژه را برای مرورگرها ارائه یا Serve می‌کند. به واقع در این مدل، درخواستی که به هیچ Api Controller ای نرسد، به Blazor تحویل میشود. Blazor نیز ابتدا به دنبال Component ای می‌گردد که برای Route مربوطه نوشته شده باشد و آن را اجرا می‌کند و سپس Response آماده، به کلاینت ارسال می‌شود. در ادامه، مرورگر فایل‌های DLL را دانلود و برنامه به صورت Single Page App به کار خود ادامه می‌دهد.
پروژه BlazorDualMode.Shared  پروژه‌ای است که کد مشترک بین Api و Blazor در آن قرار می‌گیرد. برای مثال میتوانید Dtoها را در این قسمت قرار دهید.
پروژه BlazorDualMode.Web پروژه‌ای است که در آن Component‌های Blazor قرار دارند. در حالت Server این پروژه نیز قابلیت اجرا می‌یابد و باید با امکانات Visual Studio یا IDE دلخواه خود پروژه Web و Api را به صورت همزمان اجرا کنید تا به درستی کار کند.

فایلهای Program.cs، Startup.cs و همچنین خود csproj‌ها و در نهایت فایل Host.cshtml، نهایت تفاوت این دو حالت بوده و کدهای بیزینسی پروژه و حتی Componentها و Api Controllerها در هر دو حالت یکی هستند. Configuration با شرط if server یا if client هندل شده‌اند و درک جزئیات تنظیمات مربوطه نیاز به تسلط بر روی خود Blazor را دارد که از موضوع این پست خارج است؛ ولی در صورت داشتن هر گونه سوالی، می‌توانید از قسمت پرسش و پاسخ همین سایت استفاده کنید.
مطالب
معماری لایه بندی نرم افزار #2

Domain Model یا Business Layer

پیاده سازی را از منطق تجاری یا Business Logic آغاز می‌کنیم. در روش کد نویسی Smart UI، منطق تجاری در Code Behind قرار می‌گرفت اما در روش لایه بندی، منطق تجاری و روابط بین داده‌ها در Domain Model طراحی و پیاده سازی می‌شوند. در مطالب بعدی راجع به Domain Model و الگوهای پیاده سازی آن بیشتر صحبت خواهم کرد اما بصورت خلاصه این لایه یک مدل مفهومی از سیستم می‌باشد که شامل تمامی موجودیت‌ها و روابط بین آنهاست.

الگوی Domain Model جهت سازماندهی پیچیدگی‌های موجود در منطق تجاری و روابط بین موجودیت‌ها طراحی شده است.

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

Domain Model را در پروژه SoCPatterns.Layered.Model پیاده سازی می‌کنیم. بنابراین به این پروژه یک Interface به نام IDiscountStrategy را با کد زیر اضافه نمایید:

public interface IDiscountStrategy
{
    decimal ApplyExtraDiscountsTo(decimal originalSalePrice);
}

علت این نوع نامگذاری Interface فوق، انطباق آن با الگوی Strategy Design Pattern می‌باشد که در مطالب بعدی در مورد این الگو بیشتر صحبت خواهم کرد. استفاده از این الگو نیز به این دلیل بود که این الگو مختص الگوریتم هایی است که در زمان اجرا قابل انتخاب و تغییر خواهند بود.

توجه داشته باشید که معمولا نام Design Pattern انتخاب شده برای پیاده سازی کلاس را بصورت پسوند در انتهای نام کلاس ذکر می‌کنند تا با یک نگاه، برنامه نویس بتواند الگوی مورد نظر را تشخیص دهد و مجبور به بررسی کد نباشد. البته به دلیل تشابه برخی از الگوها، امکان تشخیص الگو، در پاره ای از موارد وجود ندارد و یا به سختی امکان پذیر است.

الگوی Strategy یک الگوریتم را قادر می‌سازد تا در داخل یک کلاس کپسوله شود و در زمان اجرا به منظور تغییر رفتار شی، بین رفتارهای مختلف سوئیچ شود.

حال باید دو کلاس به منظور پیاده سازی روال تخفیف ایجاد کنیم. ابتدا کلاسی با نام TradeDiscountStrategy را با کد زیر به پروژه SoCPatterns.Layered.Model اضافه کنید:

public class TradeDiscountStrategy : IDiscountStrategy
{
    public decimal ApplyExtraDiscountsTo(decimal originalSalePrice)
    {
        return originalSalePrice * 0.95M;
    }
}

سپس با توجه به الگوی Null Object کلاسی با نام NullDiscountStrategy را با کد زیر به پروژه SoCPatterns.Layered.Model اضافه کنید:

public class NullDiscountStrategy : IDiscountStrategy
{
    public decimal ApplyExtraDiscountsTo(decimal originalSalePrice)
    {
        return originalSalePrice;
    }
}

از الگوی Null Object زمانی استفاده می‌شود که نمی‌خواهید و یا در برخی مواقع نمی‌توانید یک نمونه (Instance) معتبر را برای یک کلاس ایجاد نمایید و همچنین مایل نیستید که مقدار Null را برای یک نمونه از کلاس برگردانید. در مباحث بعدی با جزئیات بیشتری در مورد الگوها صحبت خواهم کرد.

با توجه به استراتژی‌های تخفیف کلاس Price را ایجاد کنید. کلاسی با نام Price را با کد زیر به پروژه SoCPatterns.Layered.Model اضافه کنید:

public class Price
{
    private IDiscountStrategy _discountStrategy = new NullDiscountStrategy();
    private decimal _rrp;
    private decimal _sellingPrice;
    public Price(decimal rrp, decimal sellingPrice)
    {
        _rrp = rrp;
        _sellingPrice = sellingPrice;
    }
    public void SetDiscountStrategyTo(IDiscountStrategy discountStrategy)
    {
        _discountStrategy = discountStrategy;
    }
    public decimal SellingPrice
    {
        get { return _discountStrategy.ApplyExtraDiscountsTo(_sellingPrice); }
    }
    public decimal Rrp
    {
        get { return _rrp; }
    }
    public decimal Discount
    {
        get {
            if (Rrp > SellingPrice)
                return (Rrp - SellingPrice);
            else
                return 0;
        }
    }
    public decimal Savings
    {
        get{
            if (Rrp > SellingPrice)
                return 1 - (SellingPrice / Rrp);
            else
                return 0;
        }
    }
}

کلاس Price از نوعی Dependency Injection به نام Setter Injection در متد SetDiscountStrategyTo استفاده نموده است که استراتژی تخفیف را برای کالا مشخص می‌نماید. نوع دیگری از Dependency Injection با نام Constructor Injection وجود دارد که در مباحث بعدی در مورد آن بیشتر صحبت خواهم کرد.

جهت تکمیل لایه Model، کلاس Product را با کد زیر به پروژه SoCPatterns.Layered.Model اضافه کنید:

public class Product
{
    public int Id {get; set;}
    public string Name { get; set; }
    public Price Price { get; set; }
}

موجودیت‌های تجاری ایجاد شدند اما باید روشی اتخاذ نمایید تا لایه Model نسبت به منبع داده ای بصورت مستقل عمل نماید. به سرویسی نیاز دارید که به کلاینت‌ها اجازه بدهد تا با لایه مدل در اتباط باشند و محصولات مورد نظر خود را با توجه به تخفیف اعمال شده برای رابط کاربری برگردانند. برای اینکه کلاینت‌ها قادر باشند تا نوع تخفیف را مشخص نمایند، باید یک نوع شمارشی ایجاد کنید که به عنوان پارامتر ورودی متد سرویس استفاده شود. بنابراین نوع شمارشی CustomerType را با کد زیر به پروژه SoCPatterns.Layered.Model اضافه کنید:

public enum CustomerType
{
    Standard = 0,
    Trade = 1
}

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

public static class DiscountFactory
{
    public static IDiscountStrategy GetDiscountStrategyFor
        (CustomerType customerType)
    {
        switch (customerType)
        {
            case CustomerType.Trade:
                return new TradeDiscountStrategy();
            default:
                return new NullDiscountStrategy();
        }
    }
}

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

لایه‌ی سرویس با برقراری ارتباط با منبع داده ای، داده‌های مورد نیاز خود را بر می‌گرداند. برای این منظور از الگوی Repository استفاده می‌کنیم. از آنجایی که لایه Model باید مستقل از منبع داده ای عمل کند و نیازی به شناسایی نوع منبع داده ای ندارد، جهت پیاده سازی الگوی Repository از Interface استفاده می‌شود. یک Interface به نام IProductRepository را با کد زیر به پروژه SoCPatterns.Layered.Model اضافه کنید:

public interface IProductRepository
{
    IList<Product> FindAll();
}

الگوی Repository به عنوان یک مجموعه‌ی در حافظه (In-Memory Collection) یا انباره ای از موجودیت‌های تجاری عمل می‌کند که نسبت به زیر بنای ساختاری منبع داده ای کاملا مستقل می‌باشد.

کلاس سرویس باید بتواند استراتژی تخفیف را بر روی مجموعه ای از محصولات اعمال نماید. برای این منظور باید یک Collection سفارشی ایجاد نماییم. اما من ترجیح می‌دهم از Extension Methods برای اعمال تخفیف بر روی محصولات استفاده کنم. بنابراین کلاسی به نام ProductListExtensionMethods را با کد زیر به پروژه SoCPatterns.Layered.Model اضافه کنید:

public static class ProductListExtensionMethods
{
    public static void Apply(this IList<Product> products,
                                        IDiscountStrategy discountStrategy)
    {
        foreach (Product p in products)
        {
            p.Price.SetDiscountStrategyTo(discountStrategy);
        }
    }
}

الگوی Separated Interface تضمین می‌کند که کلاینت از پیاده سازی واقعی کاملا نامطلع می‌باشد و می‌تواند برنامه نویس را به سمت Abstraction و Dependency Inversion به جای پیاده سازی واقعی سوق دهد.

حال باید کلاس Service را ایجاد کنیم تا از طریق این کلاس، کلاینت با لایه Model در ارتباط باشد. کلاسی به نام ProductService را با کد زیر به پروژه SoCPatterns.Layered.Model اضافه کنید:

public class ProductService
{
    private IProductRepository _productRepository;
    public ProductService(IProductRepository productRepository)
    {
        _productRepository = productRepository;
    }
    public IList<Product> GetAllProductsFor(CustomerType customerType)
    {
        IDiscountStrategy discountStrategy =
                                DiscountFactory.GetDiscountStrategyFor(customerType);
        IList<Product> products = _productRepository.FindAll();
        products.Apply(discountStrategy);
        return products;
    }
}

در اینجا کدنویسی منطق تجاری در Domain Model به پایان رسیده است. همانطور که گفته شد، لایه‌ی Business یا همان Domain Model به هیچ منبع داده ای خاصی وابسته نیست و به جای پیاده سازی کدهای منبع داده ای، از Interface‌ها به منظور برقراری ارتباط با پایگاه داده استفاده شده است. پیاده سازی کدهای منبع داده ای را به لایه‌ی Repository واگذار نمودیم که در بخش‌های بعدی نحوه پیاده سازی آن را مشاهده خواهید کرد. این امر موجب می‌شود تا لایه Model درگیر پیچیدگی‌ها و کد نویسی‌های منبع داده ای نشود و بتواند به صورت مستقل و فارغ از بخشهای مختلف برنامه تست شود. لایه بعدی که می‌خواهیم کد نویسی آن را آغاز کنیم، لایه‌ی Service می‌باشد.

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

اشتراک‌ها
معرفی WebAssembly بر روی Server

WebAssembly is moving beyond the browser and is pitched to become a foundational element of modern cloud-native architecture. It lets any language compile to universal binaries that run on any OS or processor, robustly sandboxed and with great performance. This session covers a new approach to running .NET in WASI environments. You’ll see how your existing .NET code could be built into WASI-compliant modules, plus the opportunities this opens. This is experimental, not yet a committed product. 

معرفی WebAssembly بر روی Server
اشتراک‌ها
بررسی تغییرات ASP.NET Core در NET 8 Preview 7.

Here’s a summary of what’s new in this preview release:

Servers & middleware
  Antiforgery middleware
API authoring
  Antiforgery integration for minimal APIs
Native AOT
  Request Delegate Generator supports interceptors feature
  Full TrimMode is used for web projects compiled with trimming enabled
  WebApplication.CreateEmptyBuilder
Blazor
  Antiforgery integration
  Server-side form handling improvements
  Auto render mode
  Register root-level cascading values
  Improved integration of interactive components with server-side rendering
  New EmptyContent parameter for Virtualize
Identity
  New bearer token authentication handler
  New API endpoints
Single page apps (SPA)
  New Visual Studio templates
 

بررسی تغییرات ASP.NET Core در NET 8 Preview 7.
اشتراک‌ها
Visual Studio 2019 version 16.1.6 منتشر شد

Security Advisory Notices

CVE-2019-1077 Visual Studio Extension Auto Update Vulnerability

An elevation of privilege vulnerability exists when the Visual Studio Extension auto-update process improperly performs certain file operations. An attacker who successfully exploited this vulnerability could delete files in arbitrary locations. To exploit this vulnerability, an attacker would require unprivileged access to a vulnerable system. The security update addresses the vulnerability by securing locations the Visual Studio Extension auto-update performs file operations in.

CVE-2019-1075 ASP.NET Core Spoofing Vulnerability

A spoofing vulnerability exists in ASP.NET Core that could lead to an open redirect. An attacker who successfully exploited the vulnerability could redirect a targeted user to a malicious website. To exploit the vulnerability, an attacker could send a link that has a specially crafted URL and convince the user to click the link.

The security update addresses the vulnerability by correcting how ASP.NET Core parses URLs. Details can be found in the .NET Core release notes.

CVE-2019-1113 WorkflowDesigner XOML deserialization allows code execution

A XOML file referencing certain types could cause random code to be executed when the XOML file is opened in Visual Studio. There is now a restriction on what types are allowed to be used in XOML files. If a XOML file containing one of the newly unauthorized types is opened, a message is displayed explaining that the type is unauthorized.

For further information, please refer to https://support.microsoft.com/en-us/help/4512190/remote-code-execution-vulnerability-if-types-are-specified-in-xoml.

Visual Studio 2019 version 16.1.6 منتشر شد
اشتراک‌ها
ویدیوی آموزشی Angular 2.0 با Typescript

Angular 2.0 will be built using the TypeScript language. It will embrace TypeScript's idioms for working with immersive web experiences in larger applications.

You can get those same benefits by working with TypeScript and Angular together. In this session, you'll learn how Angular and TypeScript work together to create single page applications. You'll see how you can leverage the features of ECMAScript 6, and still support today's browsers. You'll see how adopting TypeScript can be as easy as changing the extensions on your .js files. How you use the TypeScript features is completely in your control. 

ویدیوی آموزشی Angular 2.0 با Typescript