مسیرراه‌ها
React 16x
پیش نیاز ها
کامپوننت ها
ترکیب کامپوننت ها
طراحی یک گرید
مسیریابی 
کار با فرم ها
ارتباط با سرور
احراز هویت و اعتبارسنجی کاربران 
React Hooks  
توزیع برنامه

مدیریت پیشرفته‌ی حالت در React با Redux و Mobx   

       Redux
       MobX  

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


    حالت‌های مختلف ذخیره سازی اطلاعات در مرورگر کاربر

    Web Storage و یا Client-side storage در دو حالت کلی session storage و local storage قابل دسترسی است:
    الف) session storage
    در این حالت اطلاعات ذخیره شده‌ی در session storage، پس از بسته شدن مرورگر، به صورت خودکار حذف خواهند شد.

    ب) local storage
    اطلاعات ذخیره شده‌ی در local storage پس از بسته شدن مرورگر نیز باقی مانده و قابل دسترسی و بازیابی مجدد هستند. تاریخ انقضای آن‌ها صرفا بر اساس خالی شدن دستی کش مرورگر توسط کاربر و یا حذف دستی اطلاعات آن توسط کدهای برنامه تعیین می‌شود.

    هر دو حالت فوق به صورت ایزوله ارائه می‌شوند؛ با محدودیت حجم 10 مگابایت (جمع حجم نهایی هر دو حالت با هم، محدود به 10 مگابایت است). به این معنا که برنامه‌های هر دومین، تنها به محل ذخیره سازی خاص همان دومین دسترسی خواهند داشت.
    همچنین API دسترسی به آن‌ها synchronous است و کار کردن با آن‌ها ساده‌است.

    البته Client-side storage به دو مورد فوق خلاصه نمی‌شود و شامل File Storage ،WebSQL ،IndexedDB و کوکی‌های مرورگر نیز هست.
    - File Storage هنوز مراحل آزمایشی خودش را طی می‌کند و مناسب برنامه‌های دنیای واقعی نیست.
    - WebSQL قرار بود بر اساس بانک اطلاعاتی معروف SQLite ارائه شود؛ اما W3C در سال 2010 این استاندارد را منسوخ شده اعلام کرد و با IndexedDB جایگزین شد. دسترسی به آن async است و می‌تواند موضوع بحثی مجزا باشد.
    - کوکی‌های مرورگرها نیز یکی دیگر از روش‌های ذخیره سازی اطلاعات در مرورگرها هستند و تنها به ذخیره سازی حداکثر 4096 بایت اطلاعات محدود هستند. کوکی‌ها نیز همانند local storage پس از بسته شدن مرورگر باقی می‌مانند؛ اما برخلاف آن، دارای تاریخ انقضاء و همچنین قابلیت ارسال بین دومین‌ها را نیز دارا می‌باشند. اگر تاریخ انقضای یک کوکی تعیین نشود، همانند session storage، در پایان کار مرورگر و بسته شدن آن، حذف خواهد شد.


    تهیه یک سرویس Angular برای کار با Web Storage

    جهت کپسوله سازی نحوه‌ی کار با session storage و local storage می‌توان سرویسی را برای این‌کار تهیه کرد:
    import { Injectable } from "@angular/core";
    
    @Injectable()
    export class BrowserStorageService {
    
      getSession(key: string): any {
        const data = window.sessionStorage.getItem(key);
        return JSON.parse(data);
      }
    
      setSession(key: string, value: any): void {
        const data = value === undefined ? null : JSON.stringify(value);
        window.sessionStorage.setItem(key, data);
      }
    
      removeSession(key: string): void {
        window.sessionStorage.removeItem(key);
      }
    
      removeAllSessions(): void {
        for (const key in window.sessionStorage) {
          if (window.sessionStorage.hasOwnProperty(key)) {
            this.removeSession(key);
          }
        }
      }
    
      getLocal(key: string): any {
        const data = window.localStorage.getItem(key);
        return JSON.parse(data);
      }
    
      setLocal(key: string, value: any): void {
        const data = value === undefined ? null : JSON.stringify(value);
        window.localStorage.setItem(key, data);
      }
    
      removeLocal(key: string): void {
        window.localStorage.removeItem(key);
      }
    
      removeAllLocals(): void {
        for (const key in window.localStorage) {
          if (window.localStorage.hasOwnProperty(key)) {
            this.removeLocal(key);
          }
        }
      }
    }
    دسترسی به local storage از طریق شیء window.localStorage انجام می‌شود و کار با آن در برنامه‌های Angular، نیاز به وابستگی خاص دیگری ندارد. این مورد برای کار با session storage از طریق شیء window.sessionStorage صورت می‌گیرد. هر دو حالت، دارای متدهای setItem برای ذخیره سازی اطلاعات، getItem برای دریافت اطلاعات، بر اساس کلیدی مشخص و removeItem برای حذف اطلاعات کلیدی معلوم، هستند.
    در حالت setItem اطلاعاتی را که مرورگرها ذخیره می‌کنند باید رشته‌ای باشد. به همین جهت توسط متد JSON.stringify می‌توان یک شیء را تبدیل به رشته کرد و ذخیره نمود و در حالت getItem توسط متد JSON.parse، می‌توان این رشته را مجددا به همان شیء پیشین خود تبدیل کرد و بازگشت داد.


    محل صحیح تعریف BrowserStorageService

    همانطور که در مطلب «سازماندهی برنامه‌های Angular توسط ماژول‌ها» بررسی شد، محل صحیح تعریف این سرویس سراسری مشترک در بین کامپوننت‌ها و ماژول‌های برنامه، در CoreModule و پوشه‌ی src\app\core\browser-storage.service.ts است:
    import { BrowserStorageService } from "./browser-storage.service";
    import { NgModule } from "@angular/core";
    import { CommonModule } from "@angular/common";
    import { RouterModule } from "@angular/router";
    
    @NgModule({
      imports: [CommonModule, RouterModule],
      exports: [], // components that are used in app.component.ts will be listed here.
      declarations: [], // components that are used in app.component.ts will be listed here.
      providers: [BrowserStorageService] // singleton services of the whole app will be listed here.
    })
    export class CoreModule { };

    و CoreModule نیز به AppModule اضافه می‌شود:
    import { CoreModule } from "./core/core.module";
    
    @NgModule({
      imports:      [
    //...
        CoreModule,
    //...
        RouterModule.forRoot(appRoutes)
      ],
    //...
    })
    export class AppModule { }

    بنابراین یکی دیگر از روش‌های به اشتراک گذاری اطلاعات در بین قسمت‌های مختلف برنامه، ذخیره سازی آن‌ها در session/local storage و سپس بازیابی آن‌ها بر اساس کلیدهای مشخص آن‌ها است.


    مثالی از نحوه‌ی کاربرد BrowserStorageService

    برای آزمایش سرویس تهیه شده، از کامپوننت و قالب ذیل استفاده خواهیم کرد. در اینجا سرویس BrowserStorageService به سازنده‌ی کلاس تزریق شده‌است و سپس دو حالت session storage و local storage مورد بررسی قرار گرفته‌اند:
    import { BrowserStorageService } from "./../../core/browser-storage.service";
    import { Component, OnInit } from "@angular/core";
    
    @Component({
      selector: "app-browser-storage-sample-test",
      templateUrl: "./browser-storage-sample-test.component.html",
      styleUrls: ["./browser-storage-sample-test.component.css"]
    })
    export class BrowserStorageSampleTestComponent implements OnInit {
    
      fromSessionStorage = "";
      fromLocalStorage = ""
    
      sessionStorageKey = "sessionStorageKey1";
      localStorageKey = "localStorageKey1"
    
      constructor(private browserStorage: BrowserStorageService) { }
    
      ngOnInit() {
      }
    
      sessionStorageSetItem() {
        this.browserStorage.setSession(this.sessionStorageKey, "Val1");
      }
    
      sessionStorageGetItem() {
        this.fromSessionStorage = this.browserStorage.getSession(this.sessionStorageKey);
      }
    
      localStorageSetItem() {
        this.browserStorage.setLocal(this.localStorageKey, { key1: "val1", key2: 2 });
      }
    
      localStorageGetItem() {
        this.fromLocalStorage = JSON.stringify(this.browserStorage.getLocal(this.localStorageKey));
      }
    }
    به همراه قالب:
    <h1>Browser storage sample</h1>
    <div class="panel">
      <button class="btn btn-primary" (click)="sessionStorageSetItem()" type="button">sessionStorage -> Set Item</button>
      <button class="btn btn-success" (click)="sessionStorageGetItem()" type="button">sessionStorage -> Get Item</button>
      <div class="alert alert-info" *ngIf="fromSessionStorage">
        {{fromSessionStorage}}
      </div>
    </div>
    
    <div class="panel">
      <button class="btn btn-warning" (click)="localStorageSetItem()" type="button">localStorage -> Set Item</button>
      <button class="btn btn-success" (click)="localStorageGetItem()" type="button">localStorage -> Get Item</button>
      <div class="alert alert-info" *ngIf="fromLocalStorage">
        {{fromLocalStorage}}
      </div>
    </div>

    در این حالت اگر برنامه را اجرا کنیم، یک چنین خروجی قابل مشاهده خواهد بود:


    و اگر به برگه‌ی Application کنسول ابزارهای توسعه دهنده‌های مرورگرها نیز مراجعه کنیم، این مقادیر ثبت شده را در دو حالت استفاده‌ی از session storage و local storage، می‌توان مشاهده کرد:



    کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
    مطالب دوره‌ها
    نحوه‌ی ارتقاء برنامه‌های SignalR 1.x به SignalR 2.x
    1) اگر هم اکنون یک پروژه جدید SignalR را آغاز و از طریق NuGet وابستگی‌های آن‌را اضافه کنید، به صورت خودکار SignalR نگارش 2 را در این تاریخ دریافت خواهید کرد. این نگارش صرفا با دات نت 4 و نیم به بعد سازگار است. بنابراین اولین کاری که باید برای ارتقاء پروژه‌های SignalR 1.x به نگارش جدید انجام دهید، تغییر Target framework پروژه به نگارش 4.5 است.
    2) حذف وابستگی‌های قدیمی
     Uninstall-Package Microsoft.AspNet.SignalR -RemoveDependencies
    فرمان فوق را اگر در کنسول پاورشل نیوگت اجرا کنید، به صورت خودکار وابستگی‌های قدیمی SignalR را حذف می‌کند.
    3) نصب فایل‌های جدید SignalR
     Install-Package Microsoft.AspNet.SignalR
    برای این منظور تنها کافی است دستور فوق را اجرا نمائید.
    4) به روز رسانی ارجاعات اسکریپتی
     <script src="Scripts/jquery.signalR-2.0.0.min.js"></script>
    ارجاع به افزونه جی‌کوئری SignalR نیز باید به نگارش 2 ارتقاء یابد.
    5) حذف نحوه‌ی تعریف مسیریابی هاب‌های SignalR از فایل global.asax برنامه.
     protected void Application_Start(object sender, EventArgs e)
    {
       //RouteTable.Routes.MapHubs();
    }
    فایل یاد شده را گشوده و سطر فوق را از آن حذف کنید. سپس یک کلاس دلخواه جدید را مثلا به نام Startup، ایجاد و محتوای آن را به نحو ذیل تغییر دهید:
    using Microsoft.Owin;
    using Owin;
    
    [assembly: OwinStartup(typeof(SignalRChat.Startup))]
    namespace SignalRChat
    {   
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                app.MapSignalR();
            }
        }
    }
    این فایل به صورت خودکار در زمان آغاز برنامه‌های SignalR 2 مورد استفاده قرار می‌گیرد (با کمک ویژگی assembly: OwinStartup آن).
    اگر از آخرین نگارش VS.NET استفاده می‌کنید، این کلاس را توسط گزینه Add -> New Item -> Owin Startup Class نیز می‌توانید اضافه نمائید.
    مطالب
    تعامل و انتقال اطلاعات بین کامپوننت‌ها در Angular – بخش دوم
    در قسمت قبل نحوه انتقال اطلاعات از کامپونت پدر به فرزند را از طریق متادیتای Input@ برسی کردیم. در اینجا نکات تکمیلی را مورد بحث قرار خواهیم داد.
    همانطور که قبلا مشاهده کردید، نام متغیر تعریف شده در کامپوننت فرزند (FormIsReadOnly) به عنوان یک خصوصیت در هنگام استفاده از کامپوننت ظاهر شده و عمل انقیاد از طریق این خصوصیت FormIsReadOnly صورت می‌گیرد. در صورتیکه قصد دارید نام خصوصیت ظاهر شده در کامپوننت، با نام متغیر تعریف شده در کامپوننت فرزند متفاوت باشد، به شکل زیر عمل کنید. 
    @Input('readOnly') FormIsReadOnly: boolean;
    در این حالت عمل انقیاد از طریق خصوصیتی با نام readOnly صورت خواهد گرفت.

    ردیابی تغییرات اعمال شده بر روی خصوصیت 

    در برخی موارد لازم است بعد از انتساب مقداری از سمت کامپوننت پدر به کامپوننت فرزند و قبل از استفاده کامپوننت فرزند از آن مقدار، تغییرات یا اعتبار سنجی بر روی مقدار منتسب شده اعمال کنیم. مثلا فرض کنید کامپوننتی را به نام LeftSideMenu تعریف کرده‌اید که باز بودن یا بسته بودن آن توسط کامپوننت پدر تنظیم میشود. در اینجا لازم است همواره منتظر تغییر این خصوصیت از سمت کامپوننت پدر بود تا بلافاصله بعد از تنظیم این خصوصیت، کامپوننت فرزند نسبت به باز شدن یا بسته ماندن، عکس العمل نشان داده و بلافاصله تغییرات را اعمال کند (منو را باز کند یا ببندد). لازمه این کار ردیابی تغییرات اعمال شده از سمت کامپوننت پدر می‌باشد تا به محض تغییر، اصلاحات یا اعتبار سنجی‌های لازم بر روی آن اعمال شود. برای این کار دو راه حل وجود خواهد داشت. 
    1. ردیابی تغییرات صورت گرفته از طریق تنظیم setter به متغیر تعریف شده با متادیتای Input@
    2. پیاده سازی onChanges توسط کامپوننت فرزند جهت ردیابی تغییرات کامپوننت 


    ردیابی تغییرات از طریق تنظیم setter

    همانطور که گفته شد استفاده از کامپوننت فرزند به شکل زیر:
    <app-customer-info FormIsReadOnly="true"></app-customer-info>
    باعث خواهد شد مقدار انتساب یافته به FormIsReadOnly از جنس رشته‌ای باشد (یعنی "true"). در اینجا می‌خواهیم قبلا از اینکه مقدار، از طریق کامپوننت پدر به فرزند مقدار دهی شود، برسی کنیم در صورتیکه مقدار انتسابی از جنس boolean نبود، خطایی را صادر و برنامه نویس را برای این استفاده نادرست از کامپوننت هشیار کنیم: 
    @Component({
        selector: 'app-customer-info',
        templateUrl: './customer-info.component.html',
        styleUrls: ['./customer-info.component.css']
    })
    export class CustomerInfoComponent implements OnInit {
        private _formIsReadOnly: boolean;
    
        @Input()
        set FormIsReadOnly(value: boolean) {
            if (typeof (value) != 'boolean')
                throw new Error(`${value} type is not boolean.`);
            this._formIsReadOnly = value;
        }
    
        get FormIsReadOnly(): boolean { return this._formIsReadOnly; }
    
        constructor() { }
    
        ngOnInit() {
        }
    }
    با تنظیم setter بر روی متغیر FormIsReadOnly، لازمه‌ی تمامی تغییرات بر روی این متغیر، اجرای آن setter خواهد بود. در اینجا برسی کردیم در صورتیکه نوع مقدار (typeof(value)) از جنس boolean نباشد، خطایی صادر شود. 


    پیاده سازی onChanges توسط کامپوننت فرزند جهت ردیابی تغییرات کامپوننت 

    یکی دیگر از راه‌های تشخیص تغییرات اعمال شده بر روی کامپوننت، پیاده سازی اینترفیس onChanges توسط کامپوننت و پیاده سازی متد تعریف شده در این اینترفیس به نام ngOnChanges می‌باشد. 
    import { Component, OnInit, Input, OnChanges, SimpleChange } from '@angular/core';
    import { ICustomerInfo } from '../../core/model/ICustomerInfo';
    
    @Component({
        selector: 'app-customer-info',
        template: '<ul>< li *ngFor="let change of changeLog">{{change }}</li></ul>',
        styleUrls: ['./customer-info.component.css']
    })
    export class CustomerInfoComponent implements OnChanges {
        changeLog: string[] = [];
    
        @Input() FormIsReadOnly: boolean;
    
        ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
            let log: string[] = [];
            for (let propName in changes) {
                let changedProp = changes[propName];
                let to = JSON.stringify(changedProp.currentValue);
                if (changedProp.isFirstChange()) {
                    log.push(`Initial value of ${propName} set to ${to}`);
                } else {
                    let from = JSON.stringify(changedProp.previousValue);
                    log.push(`${propName} changed from ${from} to ${to}`);
                }
            }
            this.changeLog.push(log.join(', '));
        }
    
        constructor() { }
    }
    این کامپوننت تمامی تغییرات اعمال شده بر روی FormIsReadOnly را ردیابی کرده و نمایش خواهد داد. نمونه خروجی به شکل زیر خواهد بود. 
    •    Initial value of FormIsReadOnly set to true
    •    FormIsReadOnly changed from true to "trued"
    •    FormIsReadOnly changed from "trued" to "true"
    •    FormIsReadOnly changed from "true" to "truef"
    •    FormIsReadOnly changed from "truef" to "true"
    •    FormIsReadOnly changed from "true" to "tru"
    •    FormIsReadOnly changed from "tru" to "tr"
    •    FormIsReadOnly changed from "tr" to "t"
    •    FormIsReadOnly changed from "t" to ""
    •    FormIsReadOnly changed from "" to "t"
    •    FormIsReadOnly changed from "t" to "tr"
    •    FormIsReadOnly changed from "tr" to "tru"
    •    FormIsReadOnly changed from "tru" to "true"

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


    ادامه دارد/

    بازخوردهای دوره
    بایدها و نبایدهای استفاده از IoC Containers
    اگر ترکیبی از Service Locator و poor man's dependency injection استفاده شود چه ایراداتی دارد ؟
    مثلاً این کد
    protected readonly IUnitOfWork UnitOfWork;
    
            protected BaseOperation():this(ObjectFactory.GetInstance<IUnitOfWork>())
            {
                
            }
            protected BaseOperation(IUnitOfWork uow)
            {
                UnitOfWork = uow;
            }
    به غیر از این که کلاس مورد نظر به Container وابسته هست آیا ایراد دیگری هم هست یا خیر ؟
    نظرات مطالب
    استفاده از فایل Json برای ذخیره و بازیابی تنظیمات برنامه
    اگر داخل appsetting.json ذخیره کنیم ، چند تا امتیاز داره : 
    اول از همه اینکه میتونیم تنظیمات رو با استفاده از قابلیت‌های dependency injection به کلاس‌های مرتبط اضافه کنیم و اون کلاس رو به صورت یک dependency service استفاده کنیم.
    دوم اینکه میتونیم از قابلیت‌های environment تو کار دات نت استفاده کنیم و مقادیر متفاوت رو لحاظ کنیم.
    نقص عمده این روش فقط میتونیم از بین primitive data type‌ها نوع‌های reference type مثل string رو استفاده کنیم.
    اشتراک‌ها
    JSON Hijacking
    مطلب کاملی در زمینه JSON Hijacking
    JSON Hijacking
    نظرات مطالب
    Cookie - قسمت اول
    مطلب بسیار خوب و کاملی بود ممنون
    مطالب
    تزریق وابستگی‌ها در ASP.NET Core - بخش 4 - طول حیات سرویس ها یا Service Lifetime
    در قسمت‌های قبلی این سری، به ترتیب ابتدا در مورد مبحث تزریق وابستگی‌ها صبحت کردیم، بعد اولین سرویس‌مان را در ASP.NET Core ثبت و واکشی کردیم. در بخش سوم، تنظیمات را درون سامانه، ثبت و استفاده کردیم و حالا در این بخش می‌خواهیم به مبحث طول حیات سرویس‌ها بپردازیم.
    همانطور که گفتیم، وظیفه‌ی DI Container، ایجاد یک نمونه از سرویس درخواست شده، تزریق آن به کلاس درخواست دهنده و در انتها از بین بردن یا Dispose شیء ایجاد شده از سرویس ثبت شده‌است. بنابراین ما باید در هنگام ثبت سرویس، بر اساس تحلیل و نیاز برنامه‌ی خودمان، طول عمر سرویس/Service Life Time را مشخص کنیم.

    بصورت کلی در Microsoft Dependency Injection Container و اکثر DI Container‌های دیگر، 3 نوع کلی چرخه‌ی حیات وجود دارند که به ترتیب پایداری و طول عمر شیء ایجاد شده، در زیر آورده شده‌اند:
    •  Singleton
    •  Scoped
    •  Transient

    Singleton (یگانه)

    فقط و فقط یک شیء از سرویس ثبت شده با این طول عمر، در اولین درخواست ایجاد می‌شود و سپس در کل طول حیات برنامه، از همین شیء ایجاد شده، استفاده می‌گردد.  به همین دلیل به آن «یگانه» یا Singleton می‌گویند. هر زمانیکه این سرویس در خواست داده می‌شود، DI Container، همان یک شیء را در اختیار درخواست دهنده قرار می‌دهد و این شیء، هیچگاه از بین نمی‌رود.  به بیان دیگر، DI Container هیچگاه این شیء را از بین نمی‌برد. شیء ساخته شده از سرویس ثبت شده‌ی با حالت Singleton، بین تمامی استفاده کنندگان، به صورت اشتراکی استفاده می‌شود. این طول عمر تقریبا مشابه‌ی اشیاء ساخته شده توسط Singleton Pattern عمل می‌کند.
    با توجه به مطالب گفته شده، ویژگی‌های سرویس‌های Singleton به شرح زیر هستند:
    •   در اولین درخواست به سرویس، یک نمونه از آن ساخته می‌شود و تا پایان برنامه در حافظه نگه داشته می‌شود.
    •   در سایر درخواست‌ها، همان یک نمونه‌ی ساخته شده‌ی از سرویس، ارائه داده می‌شود. 
    •   به علت موجود بودن در حافظه، معمولا دسترسی به آن‌ها و عملکرد آن‌ها سریعتر است.
    •   بار کاری بر روی Garbage Collector فریمورک را کاهش می‌دهند.

    بنابراین در هنگام تعریف کردن یک سرویس به صورت Singleton باید نکات زیر را مد نظر قرار بدهید:
    • باید سرویس مورد نظر Thread Safe باشد .
    •  نباید استفاده کننده‌ی از این سرویس، امکان تغییر State آن را داشته باشد.
    •  اگر ساخت شیء‌ای از یک سرویس، هزینه‌ی زیادی را داشته باشد ، احتمالا Singleton کردن آن می‌تواند ایده‌ی خوبی باشد.
    •  شیء ساخته شده‌ی از این سرویس، تا زمان اجرای برنامه، بخشی از حافظه‌ی برنامه را اشغال می‌کند. پس باید حجم اشغالی در حافظه را نیز مد نظر قرار داد.
    •  تعداد دفعات استفاده را در برابر مصرف حافظه در نظر بگیرید.
    معمولا سرویس‌هایی مثل تنظیمات برنامه، از این نوع تعریف می‌شوند.

    برای ثبت یک سرویس به صورت Singleton می‌توانیم از متدهای توسعه‌ای با نام ()AddSingleton، با سربارهای مختلف بر روی IServiceCollection استفاده کنیم. علاوه بر این، در هنگام استفاده از Option Pattern، متد Configure، خودش سرویس مورد نظر را به صورت Singleton ثبت می‌کند.

    خب، به روش زیر سرویس GuidProvider را بعنوان یک Singleton  تعریف می‌کنیم:
    services.AddSingleton(services => new GuidProvider());
     اکنون این سرویس را درون اکشن Index  و کنترلر HomeController تزریق می‌کنیم:
            public HomeController(ILogger<HomeController> logger,
                IMessageServiceA messageService,
                LiteDbConfig liteDbConfig,
                GuidProvider guidHelper)
            {
                _logger = logger;
                _messageService = messageService;
                _messageService = new MessageServiceAA();
                _guidHelper = guidHelper;
            }

    حالا اگر برنامه را اجرا کنیم، می‌بینید که با تازه سازی صفحه‌ی Home/Index ، همچنان Id، برابر با یک رشته‌ی یکسان است. حتی اگر تب دیگری را در مرورگر باز کنیم و دوباره به این صفحه برویم، می‌بینیم که Id برابر همان رشته‌ی قبلی است و دلیل این موضوع، ثبت سرویس Guid Service به صورت Singleton است.


    Scoped ( محدود شده )

    به ازای هر درخواست (در اینجا معمولا درخواست‌های Http مد نظر است) یک نمونه از این سرویس ساخته می‌شود و در طول حیات این درخواست، DI Container به هر کلاسی که به این سرویس نیاز دارد، همان یک نمونه را برگشت می‌دهد و این نمونه در کل طول اجرای این درخواست، بین تمامی سرویس گیرندگان، یکسان است. هر زمانی، درخواست به پایان برسد، نمونه‌ی ساخته شده از سرویس، Disposed می‌گردد و GC می‌تواند آن را از بین ببرد.

    معمولا سرویس‌های اتصال به پایگاه داده‌ها و کار بر روی آنها که شامل خواندن، نوشتن، ویرایش، حذف می‌شوند را با طول حیات Scoped ، درون DI Container ثبت می‌کنند . EF Core به صورت پیش فرض ، Db Context را به صورت Scoped ثبت می‌کند.

    سرویس‌های Scoped در محدوده‌ی درخواست، مانند  Singleton عمل می‌کنند و شیء ساخته شده و وضعیت آن در بین تمامی سرویس‌هایی  که به آن نیاز دارند، مشترک است. بنابراین باید به این نکته در هنگام تعریف سرویس به صورت Scoped ، توجه داشته باشید.

    تمام Middleware ‌های ASP.NET Core هم فقط همان نمونه‌ی ایجاد شده از سرویس Scoped را در طی اجرای یک درخواست خاص، می‌گیرند.

    هر سرویسی که به سرویس‌های Scoped نیاز دارد، یا باید به صورت Transient و یا باید به صورت Scoped ثبت شود، تا مانع از این شویم که شیء ساخته شده، فراتر از طول حیات موردنظرمان، در حافظه بماند و از آن استفاده شود .

    برای ثبت یک سرویس به صورت Scoped می‌توانیم از متدهای توسعه‌ای با نام AddScoped() با سربارهای مختلف بر روی IServiceCollection استفاده کنیم. در اینجا از نسخه‌ای که دو پارامتر جنریک را می‌گیرد، برای ثبت یک سرویس به صورت Scoped استفاده می‌کنیم:

    services.AddScoped<IMessageServiceB, MessageServiceBA>();

    می توانیم سرویس GuidProvider را  به جای Signleton ، به صورت Scoped ثبت کنیم: 

    services.AddScoped(services => new GuidProvider());
    حال اگر برنامه را اجرا کنیم، می بینید که این بار با تازه سازی صفحه‌ی Home/Index، مقدار  Id برابر با یک رشته‌ی جدید است.  

     

    Transient (گذرا)

    به ازای هر درخواست دهنده‌ی جدید، یک نمونه‌ی جدید از سرویس، توسط DI Container ساخته می‌شود و در اختیار آن قرار می‌گیرد.

    سرویس‌هایی را به این صورت ثبت کنید که:

    •   نیاز به Thread Safe بودن داشته باشند.
    • نمی توانید طول عمر سرویس را حدس بزنید.

    سرویس‌های Transient ، کارآیی پائین‌تری دارند و سربار عملکردی زیادی بر روی Garbage Collector می گذارند؛ ولی به علت اینکه به ازای هر واکشی، یک نمونه‌ی جدید از آن‌ها ساخته می‌شود و State بین این اشیاء به اشتراک گذاشته نمی‌شود، امنیت بیشتری دارند و درک و خطایابی آنها ساده‌تر است.

    برای ثبت سرویس‌های Transient از متد توسعه‌ای AddTransient() استفاده می‌کنیم. سربارهای این متد مانند سربارهای متدهای AddSingleton() و AddScoped() است:

    services.AddTransient<IMessageServiceC, MessageServiceCA>();

     

    وابستگی‌های محصور شده

    یک سرویس نباید وابسته‌ی به سرویسی باشد که طول حیاتی کمتر از طول حیات خودش را دارد.

    برای مثال اگر درون سرویسی با طول حیات Singleton، از یک سرویس با طول حیات Transient استفاده کنیم، اینکار باعث می‌شود که یک نمونه از سرویس Transient در طول حیات برنامه، همیشه درون حافظه بماند و این ممکن است باعث خطاهای عجیبی در هنگام اجرا شود که معمولا خطایابی و رفع آن‌ها مشکل است.


    اثرات جانبی وابستگی‌های محصور شده:

    • به اشتراک گذاری تصادفی وضعیت یک شیء بین Thread ‌ها درون سرویس‌هایی که Thread Safe نیستند.
    • اشیاء، بیش از زمان پیش بینی شده‌ی برایشان، درون حافظه باقی می‌مانند.


    سرویس‌های Transient می‌توانند به سرویس‌هایی با طول حیات زیر وابستگی داشته باشند:

    •   Transient
    •   Scoped
    •   Singleton

     

    سرویس‌های Scoped می‌توانند به سرویس‌هایی با طول حیات زیر وابستگی داشته باشند:

    • Transient
    •   Scoped


    سرویس‌های Singleton می‌توانند به سرویس هایی با طول حیات زیر وابستگی داشته باشند:

    Singleton  


    می‌توانید از جدول زیر به عنوان راهنمای خلاصه شده‌ی برای استفاده‌ی امن از سرویس‌ها درون یکدیگر بهره ببرید:


    Scope Validation 

    این قابلیت که به صورت پیش فرض در حالت توسعه‌ی برنامه‌های ASP.NET Core فعال است، در زمان شروع برنامه و در Startup ، بررسی می‌کند که سرویس‌ها، وابستگی به سرویس‌هایی با طول حیاتی مناسب، داشته باشند.