نظرات مطالب
اهمیت Controller های ساده در ASP.NET MVC
-معماری n-tier یک  مدل برای توسعه  ی نرم افزار‌های انعطاف پذیر و با قابلیت استفاده‌ی مجدد می‌باشد. MVC یکArchitectural_pattern است.
-MVC در یک معماری چند لایه وظیفه‌ی لایه‌ی نمایش (Presentation) را بر عهده دارد. همانند MVP در Windows Forms یا Web Forms.
-ViewModel الگوی MVVM با ViewModel که ما در MVC داریم تفاوت زیادی دارند و ارتباطی با هم ندارند.


نظرات مطالب
مدیریت دانلود‌های همزمان از یک سایت و بحث تایم آوت
در این مورد خیر.
اگر سرور یا حتی برنامه برای این نوع حملات آماده نشده باشند، بله. این روش‌ها می‌تونه عملیات سایت رو مختل کنه. البته هدف من فقط دریافت فید از یک سایت مادر بود :)
مطلبی رو چند وقت پیش در سایت آقای Omar AL Zabir دیدم که در همین ارتباط بود. نحوه ایجاد این نوع حملات و نحوه دفاع توسط یک برنامه هوشمند که برای این موارد آماده شده:
مشاهده مطلب:
http://msmvps.com/blogs/omar/archive/2007/03/24/prevent-denial-of-service-dos-attacks-in-your-web-application.aspx
مثالی هم که در سایت ایشون برای حمله عنوان شده عملا با پیش فرض‌های دات نت (حداکثر 2 کانکشن همزمان) کار خاصی رو انجام نمیده و نهایتا timeout خواهند گرفت، مگر اینکه ...
مطالب
ارائه‌ی قالبی عمومی برای استفاده از تقویم‌های جاوااسکریپتی در Blazor
در این مطلب قصد داریم کتابخانه‌ای با قابلیت استفاده‌ی مجدد را جهت بکارگیری «PersianDatePicker یک DatePicker شمسی به زبان JavaScript که از تاریخ سرور استفاده می‌کند» ارائه دهیم. نکات ارائه شده‌ی در آن‌را می‌توان جهت تبدیل و استفاده‌ی از تمام DatePickerهای مشابه نیز بکاربرد.



نیازهای یک ورودی تاریخ سازگار با EditForm

- باید قابلیت استفاده‌ی مجدد را داشته باشد. یعنی باید به صورت یک کامپوننت مجزا و یا به صورت یک کتابخانه‌ی مجزا ارائه شود.
- باید با سیستم اعتبارسنجی EditForm یکپارچه باشد.
- باید جنریک باشد. یعنی باید بتوان در صورت نیاز DateTime ، DateTimeOffset و DateOnly و نمونه‌های nullable آن‌هارا توسط این کامپوننت دریافت کرد و ورودی و خروجی آن رشته‌ای نباشد.


نیاز به ارث‌بری از <InputBase<T جهت ارائه‌ی کامپوننت‌هایی سازگار با EditForm

تقریبا تمام کامپوننت‌های استاندارد EditForm ارائه شده‌ی توسط Blazor، از کامپوننت پایه‌ای به نام <InputBase<T مشتق می‌شوند. این کلاس، یک کلاس abstract است که قابلیت‌های بیشتری را نسبت به یک input ساده‌ی HTML ای مانند اعتبارسنجی سازگار با EditForm ارائه می‌دهد. به همین جهت توصیه می‌شود تا اگر خواستید یک کامپوننت ورودی را برای استفاده‌ی در Blazor و EditForm آن طراحی کنید، با ارث‌بری از این کلاس شروع کنید و صرفا کار را با یک input ساده، شروع نکنید.
برای استفاده‌ی از آن، ابتدای کامپوننت Blazor ما به این صورت شروع خواهد شد:
@typeparam T
@inherits InputBase<T>
که دو متد را برای بازنویسی در اختیار ما قرار می‌دهد:
    protected override bool TryParseValueFromString(
        string? value,
        [MaybeNullWhen(false)] out T result,
        [NotNullWhen(false)] out string? validationErrorMessage)
    {
      // ...
    }

    protected override string FormatValueAsString(T? value)
    {
      // ...
    }
علت وجود این دو متد، این است که مرورگرها، رشته‌ها را در اختیار ما قرار می‌دهند و ما باید راهی را برای تبدیل T به یک رشته و عکس آن را ارائه دهیم. با بازنویسی متد TryParseValueFromString، می‌توان رشته‌ی دریافتی از کاربر را تبدیل به T کرد و اگر این تبدیل میسر نبود، با مقدار دهی validationErrorMessage، مشکل را به کاربر، با یک پیام شکست اعتبارسنجی، اعلام کرد. کار متد FormatValueAsString، تبدیل T به یک رشته‌است تا در input واقع در صفحه، نمایش داده شود. در اینجا می‌توان فرمت خاصی را به شیء دریافتی اعمال و نمایش داد.


ایجاد یک کتابخانه‌ی جدید برای محصور سازی DatePicker جاوااسکریپتی

چون قصد استفاده‌ی مجدد از این کامپوننت جدید را در پروژه‌های مختلف داریم، بهتر است آن‌را تبدیل به یک «کتابخانه‌ی Blazor» کنیم. به همین جهت کتابخانه‌ی فرضی BlazorPersianJavaScriptDatePicker.Lib را در اینجا ایجاد کرده‌ایم.
در ابتدا دو فایل PersianDatePicker.js و PersianDatePicker.css موجود و مدنظر را در پوشه‌های js و css پوشه‌ی wwwroot این کتابخانه کپی می‌کنیم. بنابراین استفاده کننده‌ی از آن، مانند پروژه‌ی blazor wasm جدیدی به نام BlazorPersianJavaScriptDatePicker، باید ارجاعاتی را به آن‌ها به صورت زیر اضافه کند:
<link href="_content/BlazorPersianJavaScriptDatePicker.Lib/css/PersianDatePicker.css" rel="stylesheet"/>
<script src="_content/BlazorPersianJavaScriptDatePicker.Lib/js/PersianDatePicker.js?v=1"></script>
همچنین در فایل Imports.razor_ آن نیز بهتر است فضای نام این کتابخانه، ذکر شود تا به سادگی بتوان از کامپوننت PersianDatePicker در آن استفاده کرد:
@using BlazorPersianJavaScriptDatePicker.Lib


شروع به پیاده سازی کامپوننت PersianDatePicker

در ادامه کامپوننت جدید PersianDatePicker.razor را به پروژه‌ی کتابخانه اضافه می‌کنیم. قسمت razor آن به صورت زیر است:
@typeparam T
@inherits InputBase<T>

<div>
    <span style="cursor:pointer"
          onclick="PersianDatePicker.Show(document.getElementById('@ElementId'), '@Today')">
        📅
    </span>
    <input
        @attributes="@AdditionalAttributes"
        type="text" dir="ltr"
        @ref="ElementReference"
        name="@ElementId" id="@ElementId"
        autocapitalize="off" autocorrect="off" autocomplete="off"
       
        value="@EnteredValue"
        @oninput="OnInput"/>

    @if (ValueExpression is not null)
    {
        <ValidationMessage For="@ValueExpression"/>
    }
</div>
همانطور که مشاهده می‌کنید، کار با جنریک تعریف کردن و ارث‌بری از InputBase شروع می‌شود.
در اینجا با کلیک بر روی دکمه‌ی 📅، کار فراخوانی متد PersianDatePicker.Show مربوط به datePicker جاوا اسکریپتی صورت می‌گیرد. همچنین هر طراحی را که در اینجا ارائه دهیم، قالب UI پیش‌فرض InputBase را بازنویسی می‌کند.


نیاز به دریافت تاریخ تنظیم شده‌ی توسط کدهای جاوااسکریپتی در کامپوننت Blazor

کتابخانه‌های جاوااسکریپتی با مقداردهی مستقیم textbox.value سبب تغییر مقدار آن می‌شوند. نکته‌ی مهم اینجا است که نه فقط Blazor این تغییرات را ردیابی نمی‌کند، بلکه اگر با استفاده از متد استاندارد جاوااسکریپتی addEventListener به تغییرات این input گوش فرا دهیم، هیچ رخدادی را مشاهده نخواهیم کرد. به همین جهت نیاز است اندکی کدهای PersianDatePicker.js را تغییر دهیم (و این مورد جهت تمام کتابخانه‌های مشابه یکسان است):
    function setValue(date) {
        _textBox.value = date;

        // NOTE: To notify the addEventListener('change', fn)
        _textBox.dispatchEvent(new Event('change'));

        _textBox.focus();
        hide();
        try {
            _textBox.onchange();
        }catch(ex) {}
    }
در اینجا پس از تغییر خاصیت value، باید به صورت دستی سبب بروز رخداد change شد تا addEventListenerها بتوانند این رخداد را ردیابی کنند. به همین جهت فایل مجزایی را به نام wwwroot\js\activateDatePicker.js به کتابخانه‌ی blazor اضافه می‌کنیم:
window.activateDatePicker = {
  enableDatePicker: function (element, objectReference) {
       element.addEventListener('change', function (evt) {    
            objectReference.invokeMethodAsync("OnInputFieldChanged", this.value);
       });
  }
};
هدف از این کدها این است که جهت element یا همان datePicker جاری، بتوان رخ‌داد change را ثبت کرد و به تغییرات آن گوش فرا داد تا هر زمانیکه کدهای جاوا اسکریپتی datePicker سبب تغییری در خاصیت value شدند، بتوان آن‌را به کامپوننت Blazor ارسال کرد. وهله‌ای از این کامپوننت توسط objectReference در اینجا دریافت شده و سپس متد OnInputFieldChanged کامپوننت را با مقدار جدید وارد شده، فراخوانی می‌کند.
بنابراین این فایل جدید نیز باید به index.html مصرف کننده اضافه شود:
<script src="_content/BlazorPersianJavaScriptDatePicker.Lib/js/activateDatePicker.js?v=1"></script>


فعالسازی DatePicker در اولین بار نمایش کامپوننت Blazor

تا اینجا زیرساخت دریافت مقدار تنظیمی توسط کاربر را در کامپوننت Blazor فراهم کردیم. اکنون نوبت به استفاده‌ی از آن است:
public partial class PersianDatePicker<T> : IDisposable
{
    private bool _isDisposed;
    private DotNetObjectReference<PersianDatePicker<T>>? _objectReference;
    private string ElementId { get; } = Guid.NewGuid().ToString("N");
    private ElementReference? ElementReference { set; get; }
    private string Today { get; } = DateTime.Now.ToShortPersianDateString();

    [Inject] private IJSRuntime JsRuntime { set; get; } = default!;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            _objectReference = DotNetObjectReference.Create(this);
            await JsRuntime.InvokeVoidAsync("activateDatePicker.enableDatePicker", ElementReference, _objectReference);
            EnteredValue = CurrentValueAsString;
            StateHasChanged();
        }
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        if (!_isDisposed)
        {
            try
            {
                _objectReference?.Dispose();
            }
            finally
            {
                _isDisposed = true;
            }
        }
    }
}
- اگر دقت کرده باشید در تعاریف razor کامپوننت، "ref="ElementReference@ وجود دارد که یک ElementReference است و توسط آن می‌توان در متد OnAfterRenderAsync، ارجاعی را به المان جاری، به کدهای جاوااسکریپتی متد enableDatePicker ارسال کرد.
- همچنین چون نمی‌خواهیم متد OnInputFieldChanged را به صورت static تعریف کنیم، نیاز است تا یک DotNetObjectReference را ایجاد و به متد enableDatePicker ارسال کرد تا توسط آن بتوان به یک instance method کلاس جاری دسترسی یافت و به سادگی مقادیر کامپوننت را تغییر داد:
[JSInvokable]
public void OnInputFieldChanged(string? value)
- در پایان کار کامپوننت، باید این DotNetObjectReference را Dispose کرد.


نیاز به تبدیل T به تاریخ رشته‌ای و برعکس

زیر ساخت تبدیلات جنریک تاریخ میلادی به شمسی در کتابخانه‌ی « DNTPersianUtils.Core » پیش‌بینی شده‌است و فقط کافی است از آن استفاده کنیم. با وجود این زیرساخت، تهیه‌ی کامپوننت‌های جنریک تاریخ شمسی بسیار ساده می‌شود:
public partial class PersianDatePicker<T> : IDisposable
{
    private string? _enteredValue;

    private string? EnteredValue
    {
        set => _enteredValue = value;
        get => UsePersianNumbers ? _enteredValue.ToPersianNumbers() : _enteredValue;
    }

    [Parameter] public bool UsePersianNumbers { set; get; }

    [Parameter] public string ParsingErrorMessage { get; set; } = "لطفا در ورودی {0} تاریخ شمسی معتبری را وارد نمائید.";

    [Parameter] public int BeginningOfCentury { set; get; } = 1400;

    private void OnInput(ChangeEventArgs e)
    {
        SetCurrentValue(e.Value as string);
    }

    private void SetCurrentValue(string? value)
    {
        EnteredValue = value;
        CurrentValueAsString = value;
    }

    [JSInvokable]
    public void OnInputFieldChanged(string? value)
    {
        SetCurrentValue(value);
    }

    protected override void OnInitialized()
    {
        base.OnInitialized();
        SanityCheck();
    }

    protected override bool TryParseValueFromString(
        string? value,
        [MaybeNullWhen(false)] out T result,
        [NotNullWhen(false)] out string? validationErrorMessage)
    {
        validationErrorMessage = string.Format(CultureInfo.InvariantCulture, ParsingErrorMessage, DisplayName);
        if (!value.TryParsePersianDateToDateTimeOrDateTimeOffset(out result, BeginningOfCentury))
        {
            return false;
        }

        if (result is null)
        {
            throw new InvalidOperationException(validationErrorMessage);
        }

        validationErrorMessage = null;
        return true;
    }

    protected override string FormatValueAsString(T? value)
    {
        return !string.IsNullOrWhiteSpace(EnteredValue) ? EnteredValue : value.FormatDateToShortPersianDate();
    }

    private void SanityCheck()
    {
        if (!Value.IsDateTimeOrDateTimeOffsetType())
        {
            throw new InvalidOperationException(
                "The `Value` type is not a supported `date` type. DateTime, DateTime?, DateTimeOffset and DateTimeOffset? are supported.");
        }
    }

    // ...
}
در اینجا قسمت نهایی و تکمیلی کامپوننت محصور کننده‌ی DatePicker را مشاهده می‌کنید که بسیار ساده‌است:
- InputBase به همراه یک خاصیت عمومی دوطرفه‌ی Value است که امکان تعریفی مانند bind-Value@ را میسر می‌کند.
- این Value به همراه یک خاصیت متناظر رشته‌ای به نام CurrentValueAsString نیز هست که در اینجا از آن استفاده می‌کنیم و کار با آن، بایندینگ دوطرفه و همچنین اعتبارسنجی خودکار و فعالسازی متدهای بازنویسی شده‌ی InputBase را میسر می‌کند.
- پیاده سازی متدهای بازنویسی شده‌ی جنریک TryParseValueFromString و FormatValueAsString، با استفاده از دو متد TryParsePersianDateToDateTimeOrDateTimeOffset و FormatDateToShortPersianDate کتابخانه‌ی « DNTPersianUtils.Core » انجام شده‌اند و اصل کار تهیه‌ی یک کامپوننت جنریک تاریخ شمسی را انجام می‌دهند.


استفاده‌ی از کامپوننت Blazor تهیه شده‌

یک کامپوننت تاریخ شمسی باید بتواند تمام حالات و نوع‌های زیر را پوشش دهد که به لطف جنریک بودن کامپوننت تهیه شده، این امر میسر است:
using System.ComponentModel.DataAnnotations;

namespace BlazorPersianJavaScriptDatePicker.ViewModels;

public class InputPersianDateViewModel
{
    [Required] public string Name { set; get; } = default!;

    [Required] public DateTime BirthDayGregorian { set; get; } = DateTime.Now.AddYears(-40);

    public DateTime? LoginAt { set; get; } = DateTime.Now.AddMinutes(-2);

    [Required] public DateTimeOffset LogoutAt { set; get; }

    public DateTimeOffset? RegisterAt { set; get; } = DateTimeOffset.Now.AddMinutes(-10);
}
سپس از این کامپوننت، در صفحه‌ی Index مثال پیوست به صورت زیر استفاده شده‌است:
<EditForm Model="Model" OnValidSubmit="DoSave">
    <DataAnnotationsValidator/>

    <div>
        <label>تاریخ تولد</label>
        <div>
            <PersianDatePicker
                @bind-Value="Model.BirthDayGregorian"
                UsePersianNumbers="false"
               />
        </div>
    </div>

    <button type="submit">ارسال</button>
</EditForm>


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید:  BlazorPersianJavaScriptDatePicker.zip
اشتراک‌ها
مقایسه کارآیی بین C# Wasm AOT ،C# Wasm Interpreted ،C# Runtime و JavaScript

The conclusion of the analysis:
C# Wasm AOT still has a long way to become a general and performant client side web programming platform. Note: The usage of the Uno.Wasm.Bootstrap toolchain may have affected the performance of some of the benchmarks. Thus, this analysis should not be regarded as a benchmark of the finalized product. However, note that the Uno platform is using ".NET 6 WebAssembly Mixed mode AOT/Interpreter" (source). 

مقایسه کارآیی بین C# Wasm AOT ،C# Wasm Interpreted ،C# Runtime و JavaScript
اشتراک‌ها
بهترین تمرینها برای ASP.NET Core Web API

In this post, we are going to write about what we consider to be the best practices while developing the .NET Core Web API project. How we can make it better and how to make it more maintainable.

We are going to go through the following sections:

بهترین تمرینها برای ASP.NET Core Web API
اشتراک‌ها
کتاب رایگان Scala Succinctly

Learning a new programming language can be a daunting task, but Scala Succinctly makes it a simple matter. Author Chris Rose guides readers through the basics of Scala, from installation to syntax shorthand, so that they can get up and running quickly.

Table of Contents
  1. Introduction
  2. Variables and Values
  3. Expressions and Functions
  4. Control Structures
  5. Arrays and Lists
  6. Other Collection Types
  7. Classes and Objects
  8. Pattern Matching
  9. Closures
  10. Conclusion 
کتاب رایگان Scala Succinctly
نظرات مطالب
تبدیل زیرنویس‌های خاص پلورال‌سایت به فرمت SRT
ظاهراً ساختار عوض شده به این شکل (البته در اینجا data-s حذف شده و مقدار آن به صورت رشته ایی در انتهای مقدار ng-click اضافه شده است به صورت start=39.796) :
<li class="transcript-clip">
<a href="javascript:void(0)" ng-click="launchPlayerWindow('http://pluralsight.com/training', 'author=scott-allen&amp;name=mvc4-building-m1-intro&amp;mode=live&amp;clip=0&amp;course=mvc4-building');">Introduction</a><br>
    <div>
<a href="javascript:void(0)" ng-click="launchPlayerWindow('http://pluralsight.com/training', 'author=scott-allen&amp;name=mvc4-building-m1-intro&amp;mode=live&amp;clip=0&amp;course=mvc4-building&amp;start=39.796');">and also have an understanding of the design goals of the MVC framework.</a>
        <a href="javascript:void(0)" ng-click="launchPlayerWindow('http://pluralsight.com/training', 'author=scott-allen&amp;name=mvc4-building-m1-intro&amp;mode=live&amp;clip=0&amp;course=mvc4-building&amp;start=43.796');">So, let's get started.</a>
    </div>
</li>

مطالب
سفارشی کردن Controller factory توکار و فرصتی برای تزریق وابستگی‌ها به کنترلر
در مقاله‌ی قبلی ( + ) به این لحاظ که بهترین راه نشان دادن نحوه‌ی کارکرد Controller Factory ایجاد یک نمونه‌ی سفارشی بود، آن رابررسی کردیم و برای اکثریت برنامه‌ها و سناریوها، کلاس توکار Controller Factory به نام DefaultControllerFactory کفایت می‌کند.
پس از وصول یک درخواست از طریق سیستم مسیریابی، factory پیش فرض (DefaultControllerFactory) به بررسی rout data پرداخته تا خاصیت Controller آن را بیابد و سعی در پیدا کردن کلاسی در برنامه خواهد داشت که مشخصات ذیل را دارا باشد:
  1. دارای سطح دسترسی public باشد.
  2. Abstract نباشد.
  3. حاوی پارامتر generic نباشد.
  4. نام کلاس دارای پسوند Controller باشد.
  5. پیاده سازی کننده اینترفیس IContoller باشد.
کلاس DefaultControllerFactory در صورت یافتن کلاسی مطابق قواعد فوق و مناسب درخواست رسیده، وهله‌ای از آن را به کمک Controller Activator ایجاد می‌کند. می‌بینید که با برپایی چند قاعده‌ی ساده،  factory پیش فرض، نیاز به ثبت کنترلرها را به منظور معرفی و داشتن لیستی برای بررسی از طرف برنامه نویس (مثلا درج نام کلاس‌های کنترلر در یک فایل پیکربندی)، مرتفع ساخته است.
اگر بخواهید به فضاهای نام خاصی برای یافتن آنها توسط factory پیش فرض، برتری قائل شوید، باید در متد Application_Start فایل global.asax.cs مانند ذیل عمل نمایید:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using ControllerExtensibility.Infrastructure;
namespace ControllerExtensibility
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.DefaultNamespaces.Add("MyControllerNamespace");
            ControllerBuilder.Current.DefaultNamespaces.Add("MyProject.*");
        }
    }
}

سفارشی کردن وهله سازی کنترلرها توسط DefaultControllerFactory

مهمترین دلیلی که نیاز داریم factory پیش فرض را سفارشی کنیم، استفاده از تزریق وابستگی‌ها (DI) به کنترلرهاست. راه‌های متعددی برای این کار وجود دارند که انتخاب بهترین روش بسته به چگونگی بکارگیری DI در برنامه شماست:
الف) تزریق وابستگی به کنترلر با ایجاد یک controller activator سفارشی

کدهای اینترفیس  IControllerActivator  مطابق ذیل است:
namespace System.Web.Mvc
{
    using System.Web.Routing;
    public interface IControllerActivator
    {
        IController Create(RequestContext requestContext, Type controllerType);
    }
}
این اینترفیس حاوی متدی به نام Create است که شیء RequestContext به آن پاس داده می‌شود و یک Type که مشخص می‌کند کدام کنترلر باید وهله سازی شود. در کدهای ذیل در قسمت (return (IController)ObjectFactory.GetInstance(controllerType  فرض بر این است که در پروژه برای تزریق وابستگی، StructureMapFactory  را به کار گرفته‌ایم و سیم کشی‌های لازم قبلا صورت گرفته است. چنانچه با StructureMap  آشنایی ندارید به این مقاله سایت (استفاده از StructureMap به عنوان یک IoC Container) مراجعه نمایید.
using ControllerExtensibility.Controllers;
using System;
using System.Web.Mvc;
using System.Web.Routing;
namespace ControllerExtensibility.Infrastructure
{
    public class StructureMapControllerActivator : IControllerActivator
    {
        public IController Create(RequestContext requestContext,
        Type controllerType)
        {
            return (IController)ObjectFactory.GetInstance(controllerType);
        }
    }
}

در شکل فوق منظور از CustomControllerActivator یک پیاده سازی از اینترفیس IControllerActivator مانند کلاس StructureMapControllerActivator است.
برای استفاده از این activator سفارشی نیاز داریم وهله‌ای از آن را به عنوان پارامتر به سازنده‌ی کلاس DefaultControllerFactory ارسال کنیم و نتیجه را در متد Application_Start فایل global.asax.cs ثبت کنیم.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using ControllerExtensibility.Infrastructure;
namespace ControllerExtensibility
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            ControllerBuilder.Current.SetControllerFactory(new
            DefaultControllerFactory(new StructureMapControllerActivator()));
        }
    }
}

ب) تحریف و بازنویسی متدهای کلاس DefaultControllerFactory
 می‌توان متدهای کلاس مشتق شده‌ی از DefaultControllerFactory را override کرد و برای اهدافی نظیر DI از آن بهره جست. جدول ذیل سه متدی که می‌توان با تحریف آنها به مقصود رسید، توصیف شده‌اند:

 متد  نوع بازگشتی توضیحات
  CreateController   IController  پیاده سازی کننده‌ی متد Createontroller از اینترفیس IControllerFactory است و به صورت پیش فرض متد GetControllerType را جهت تعیین نوعی که باید وهله سازی شود، صدا می‌زند و سپس کنترلر وهله سازی شده را به متد GetControllerInstance ارسال می‌کند.
  GetControllerType   Type  وظیفه‌ی نگاشت درخواست رسیده را به Controller type عهده دار است.
GetControllerInstance IController وظیفه ایجاد وهله‌ای از نوع مشخص شده را عهده دار است.

شیوه‌ی تحریف متد GetControllerInstance 
public class StructureMapControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            return ObjectFactory.GetInstance(controllerType) as Controller;
        }
    }
شیوه‌ی ثبت در فایل global.asax.cs و در متد Application_start :
 ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());

نمونه‌ای عملی آن‌را در مقاله‌ی (EF Code First #12) و یا دوره‌ی «بررسی مفاهیم معکوس سازی وابستگی‌ها و ابزارهای مرتبط با آن» می‌توانید بررسی کنید.
اشتراک‌ها
آموزش Flexbox در CSS3
 The Flexbox Layout officially called CSS Flexible Box Layout Module is new layout module in CSS3 made to improve the items align, directions and order in the container even when they are with dynamic or even unknown size. 
آموزش Flexbox در CSS3
مطالب
تزریق وابستگی‌ها در ASP.NET Core - بخش 3 - ثبت و واکشی تنظیمات
همانطور که پیشتر گفتیم، Dependency Injection Container، ماژول اصلی ASP.NET Core است. تقریبا تمامی ماژول‌ها و سرویس‌های ASP.NET Core از DI Container Injection استفاده می‌کنند که بعضی از آنها عبارتند از:
  •   Configuration
  •   Routing
  •   MVC
  •   Application
  • و ...
بصورت درونی، چارچوب/ فریم ورک ASP.NET Core، مسئول ارائه‌ی وابستگی‌ها، در زمان فعال سازی ماژول‌های خود فریم ورک ASP.NET Core می‌باشد.
فرض کنید یک درخواست برای صفحه‌ی اول سایت به وبسایتی بر پایه‌ی ASP.NET Core می‌رسد. به صورت گام به گام، این مراحل برای پردازش داده به کار می‌روند:
  1. کاربر یک درخواست Http را توسط مرورگر ارسال می‌کند.
  2. یکی از اولین میان افزار‌ها یعنی میان افزار Routing، آدرس درخواست را می‌خواند، کنترلر و اکشن مورد نظر را می‌یابد و به‌وسیله‌ی Activator Utility، سعی در فعال سازی آن کنترلر می‌کند. 
  3.   DI Container لیست پارامترهای سازنده‌ی کنترلر را مشاهده می‌کند و سرویس‌های مورد نیاز را از درون خود واکشی کرده، از آنها نمونه سازی می‌کند و نمونه‌های ساخته شده را  به درون شیء کنترلر تزریق می‌کند.
  4.  Routing درخواست HttpRequest را تجزیه کرده و اکشن متد مورد نظر را برای اجرای آن فراخوانی کرده
  5. و نتیجه‌ی اجرای اکشن را به درخواست دهنده بر می‌گرداند.

هر چند که کنترلرها درون DI Container ثبت نشده‌اند، ولی توسط کلاس‌هایی درون فریم ورک، از آنها نمونه سازی می‌شود و در حین نمونه سازی، DI Container سرویس‌های مورد نظر آن‌ها را در صورت وجود، فراهم می‌کند.

ثبت تنظیمات وبسایت و فراخوانی آنها در برنامه
در تمام برنامه‌های ASP.NET Core شما نیاز به تنظیماتی برای پیکربندی کار برنامه‌ی خود دارید. این تنظیمات می‌توانند شامل Connection String اتصال به پایگاه داده، تنظیمات اتصال به سرویس‌های خارجی مثل درگاه‌های پرداخت آنلاین بانک‌ها و ... باشند. در اینجا ما تنظیمات اختصاصی را درون فایل AppSetting اضافه می‌کنیم. بعد برای هر بخش از تنظیمات، در پوشه‌ی Configs یک کلاس ساده‌ی سی شارپ را می‌سازیم  و سپس با گرفتن و تزریق کردن این فایل‌های Config درون DI Container، هر زمانی خواستیم، از آنها استفاده می‌کنیم.
ابتدا به سراغ تنظیمات کلی می‌رویم و دو تنظیم نام برنامه و پیغام خوش آمد گویی را به برنامه اضافه می‌کنیم (فایل appSettings را به صورت زیر تغییر می‌دهیم) :
"ApplicationName": "Dependency Injection Demo",
"GreetingMessage": "Welcome to Dependency Injection Demo",
"AllowedHosts": "*",

"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},

برای سادگی کار، با بخش Logging کاری نداریم . اکنون فایل AppConfig.cs را به برنامه اضافه می‌کنیم:

namespace AspNetCoreDependencyInjection.Configs
{
    public class AppConfig
    {
        public string ApplicationName { get; set; }
        public string GreetingMessage { get; set; }
        public string AllowedHosts { get; set; }
    }
}

برای دسترسی بهتر می‌توانیم سازنده‌ی کلاس Startup را تغییر دهیم:

public IWebHostEnvironment Environment { get; }
public IConfiguration Configuration { get; }
public IServiceCollection Services { get; set; }

public Startup(IWebHostEnvironment environment)
{
var builder = new ConfigurationBuilder()
        .SetBasePath(environment.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true)
        .AddEnvironmentVariables();
        this.Environment = environment;
        this.Configuration = builder.Build();
}
کد بالا برای زمانی کاربرد دارد که شما بخواهید چند تنظیمات مختلف را در برنامه داشته باشید؛ مثلا در کد بالا در هنگام ساخت متغیر builder، می‌توانید با چک کردن متغیر environment، یک تنظیمات دیگر را داشته باشید (داشتن دو یا چند تنظیمات به خصوص برای زمان  توسعه و انتشار برنامه ضروری است. در ساده‌ترین کاربرد، شما در حالت توسعه به یک پایگاه داده تست وصل می‌شوید، ولی در حالت انتشار به پایگاه داده‌ی اصلی متصل خواهید شد). در اینجا یکی از  ساده‌ترین روش‌ها، استفاده از دو فایل تنظیمات مختلف برای زمان انتشار و غیر انتشار ( توسعه و Staging ) است:
var appSettingsFile = environment.IsProduction() ? "appsettings.json" : "appsettings_dev.json";
var builder = new ConfigurationBuilder()
.SetBasePath(environment.ContentRootPath)
                .AddJsonFile( appSettingsFile , optional: true)
                .AddEnvironmentVariables();
حالا که این تغییرات را انجام دادیم، دوباره به سراغ ثبت سرویس تنظیمات برنامه می‌رویم. برای اینکار در متد ConfigureServices و زیر خط‌های کد قبلی، این خطوط کد را اضافه می‌کنیم: 
services.AddSingleton(services => new AppConfig { 
    ApplicationName = this.Configuration["ApplicationName"],
    GreetingMessage = this.Configuration["GreetingMessage"],
    AllowedHosts = this.Configuration["AllowedHosts"]
});

در کد بالا در هنگام اجرای برنامه، یک نمونه از کلاس AppConfig را با طول حیات Singleton ثبت کردیم و Property ‌های این شیء را به وسیله‌ی ایندکس Configuration[“FieldName”]، تک تک پر کردیم.

حالا می‌توانیم سرویس AppConfig را در هر کلاسی از برنامه‌ی خودمان تزریق و از آن استفاده کنیم. برای مثل در اینجا یک کنترلر به نام AppSettingsController ساختم و کلاس فوق را به آن تزریق کردم: 

public class AppSettingsController : Controller
{
        private readonly AppConfig _appConfig;
        public AppSettingsController(AppConfig appConfig)
        {
            _appConfig = appConfig;
        } 
 // codes here …
}

می توانیم از همین الگو برای تعریف، ثبت و استفاده از سایر تنظیمات نیز استفاده کنیم:
"UserOptionConfig": {
    "UsersAvatarsFolder": "avatars",
    "UserDefaultPhoto": "icon-user-default.png",
    "UserAvatarImageOptions": {
         "MaxWidth": 150,
         "MaxHeight": 150
    }
},

"LiteDbConfig": {
   "ConnectionString": "Filename=\\Data\\DependencyInjectionDemo.db;Connection=direct;Password=@123456;"
}

برای LiteDbConfig مانند AppConfig عمل می‌کنیم، ولی در هنگام ثبت آن، به روش زیر عمل می‌کنیم. تنها تفاوتی که وجود دارد، نحوه‌ی دستیابی به فیلدهای درونی فایل JSON به وسیله‌ی شیء Configuration است: 

services.AddSingleton(services => new LiteDbConfig
{
    ConnectionString = this.Configuration["LiteDbConfig:ConnectionString"],
});

اکنون برای استفاده‌ی از مدخل UserOptionConfig، کلاس‌های زیر را می‌سازیم:

namespace AspNetCoreDependencyInjection.Configs
{
    public class UserOptionConfig
    {
        public string UsersAvatarsFolder { get; set; }
        public string UserDefaultPhoto { get; set; }
        public UserAvatarImageOptions UserAvatarImageOptions { get; set; }
    }

    public class UserAvatarImageOptions
    {
        public int MaxHeight { get; set; }
        public int MaxWidth { get; set; }
    }
}
می‌خواهیم روش Option Pattern را که روش توصیه  شده‌ی Microsoft برای استفاده از پیکربندی برنامه است، بکار ببریم. به صورت خلاصه، Option Pattern بیان می‌کند که بخش‌های مختلف پیکربندی تنظیمات برنامه را از یکدیگر جدا کنیم و به ازای هر بخش، کلاس‌های مختص به خود را داشته باشیم و با ثبت جداگانه‌ی آنها در DI Container ، از  آن‌ها استفاده کنیم.

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

  • Interface Segregation Principle (ISP) or Encapsulation : کلاس‌هایی که به تنظیمات نیاز دارند، فقط به آن بخشی از تنظیمات دسترسی خواهند داشتند که واقعا مورد نیازشان باشد.
  •   Separation Of Concerns : تنظیمات بخش‌های مختلف برنامه، به یکدیگر وابسته و  جفت شده نیستند.

در اینجا  نیاز به استفاده از پکیج Microsoft.Extensions.Options.ConfigurationExtensions را داریم که به صورت درونی در ASP.NET Core تعبیه شده است.

برای ثبت این تنظیمات درون DI Container، از نمونه‌ی جنریک متد Configure در IServiceCollection به صورت زیر استفاده می‌کنیم:

services.Configure<UserOptionConfig>(this.Configuration.GetSection("UserOptionConfig"));

متد GetSection بر اساس نام بخش تنظیمات، خود آن تنظیم و تمامی تنظیمات درونی آن را به صورت یک IConfigurationSection بر می‌گرداند و متد Configure<TOption> یک IConfiguration را گرفته و به صورت خودکار به TOption اتصال می‌دهد و سپس این شیء را درون DI Container به عنوان یک IConfigurationOptions<TOption> و با طول حیات Singleton ثبت می‌کند.

برای دسترس به UserOptionConfig درون کلاس مورد نظر ما، اینترفیس <IOptionMonitor<TOption را به سازنده‌ی کلاس مورد نظر تزریق می‌کنیم. کد زیر را که نسخه‌ی تغییر یافته‌ی کلاس AppSettingsController است را مشاهده کنید: 
private readonly LiteDbConfig _liteDbConfig;
private readonly AppConfig _appConfig;
private readonly UserOptionConfig _userOptionConfig; 

public AppSettingsController(AppConfig appConfig ,
    LiteDbConfig liteDbConfig ,
    IOptionsMonitor<UserOptionConfig> userOptionConfig)
{
    _appConfig = appConfig;
    _liteDbConfig = liteDbConfig;
    _userOptionConfig = userOptionConfig.CurrentValue;
}
در اینجا و در سازنده برای گرفتن TOption ، از CurrentValue که یک property تعریف شده‌ی درون IOptionsMonitor<TOption> است، استفاده می‌کنیم.

نکته ای که وجود دارد، کلاس‌های تعریف شده برای استفاده‌ی از این الگو باید شرایط زیر را داشته باشند ( مثل کلاس UserOptionConfig ) :

  • باید سطح دسترسی public داشته باشند.
  • باید دارای سازنده‌ی پیش فرض باشند.
  •   باید نام Property ‌های آنها دقیقا همنام فیلدهای تنظیمات باشد تا فرایند mapping خودکار به درستی انجام شود.
  •   باید Property ها و Setter آنها ، سطح دسترسی public داشته باشند.

هر دو روش بالا که یکی به صورت عادی تنظیمات را ثبت می‌کند و دیگری با استفاده از Option Pattern بخش‌های مختلف را ثبت می‌کند، مناسب هستند. البته گاهی اوقات فایل‌های تنظیمات پروژه‌ی شما در لایه‌های زیرین (یا درونی‌تر اگر از onion architecture استفاده می‌کنید) قرار دارند و شما نمی‌خواهید در آن لایه‌ها و لایه‌های درونی‌تر، وابستگی به پکیج‌های ASP.NET Core ایجاد کنید. در این حالت با در نظر گرفتن دو اصل ISP و Separation of Concerns ، به ازای هر بخش مختلف از تنظیمات، فایل‌های تنظیمات را در لایه‌های زیرین/درونی تعریف کرده، بعد در لایه‌های بالاتر/بیرونی‌تر آنها را به درون سرویس‌ها یا کلاس‌های مورد نیاز، تزریق کنید. البته مثل همین مثال، ثبت این سرویس‌ها درون برنامه‌ی ASP.NET Core که معمولا بالاترین/بیرونی‌ترین لایه از پروژه‌ی ما هست، انجام می‌شود.