تزریق وابستگی در ASP.NET MVC 6

دوشنبه 16 فروردین 1395

در حالت سنتی و عدم استفاده از تزریق وابستگی ، بین کلاس های برنامه اتصالات مستقیم وجود دارد و با new کردن آنها این کار را انجام می دهیم .در این مقاله قصد داریم تا تزریق وابستگی در MVC6 را آموزش دهیم .

تزریق وابستگی در ASP.NET MVC 6

در حالت سنتی و عدم استفاده از تزریق وابستگی ، بین کلاس های برنامه اتصالات مستقیم وجود دارد و با new کردن آنها این کار را انجام می دهیم .

اساس کار تزریق وابستگی بر اینترفیس استوار است .برای هر کلاسی که در سیستم دارید و می خواهید از آن استفاده کنید ابتدا باید اینترفیس آن را تعریف کنید .و بعد کلاس خود را با پیاده سازی این اینترفیس بسازید.

برای استفاده از این کلاس هم باید از Constructor Injection و یا Property Injection استفاده کنید تا کلاسی که قصد استفاده از آن را دارید معرفی کنید .

DIContainer های مختلفی وجود دارند مانند StructureMap  ، Ninject   با کمک این DIContainer شما تعیین خواهید کرد که کدام اینترفیس بر روی کدام کلاس شما قرار بگیرد.کاری که DIContainer انجام می دهد این است که وقتی به کلاسی نیاز دارید این ابزار از روی اینترفیس هایی که برای آن کلاس در نظر گرفته اید یک نمونه از کلاس می سازد.و آن را به کسی که قصد استفاده از کلاس را دارد تزریق می کند .
در واقع در تزریق وابستگی کاری که می کنیم این است که باید برای هر کلاس اینترفیس آن را بسازید و در هر جا به کلاس نیاز داشتید آن اینترفیس را معرفی کنید .

سپس با کمک ابزار DIContainer که انواع مختلفی از آن وجود دارد ( مانند Ninject یا StructureMap و یا  Ninject) تعیین می کنید که کدام کلاس به کدام اینترفیس map شود .
فاز توسعه سیستم در برابر فاز نگه داری آن اهمیت کمتری دارد اینکه شما سیستمی طراحی کنید که پشتیبانی آسانی داشته باشد و اگر فرد جدیدی استخدام شد به راحتی بتواند با این سیستم کار کند از اهمیت بالایی برخوردار است .

کارهای زیادی برای بالا بردن قابلیت نگه داری سیستم می توان انجام داد یکی از این کارها این است که وظیفه هر کلاس مشخص و منحصر به فرد باشد یعنی هر کلاسی یک وظیفه داشته باشد .این اصل، اصل اول کد نویسی به نام Single Responsibility است .وقتی کلاسی مثلا کلاس کنترلر برای کار کردن به کلاس های دیگر وابسته باشد به این حالت Dependency می گوییم .برای استفاده از این کلاس های Dependency روش های مختلفی وجود دارد که یکی از آنها استفاده سازنده کلاسی که به این کلاس ها وابسته می باشد است .راه دیگر استفاده از Dependency Injection است .که شما در این روش به جای اینکه از کلاس وابسته شی ایجاد کنید آن را به داخل کلاسی که به آنها وابسته است تزریق کنید .وقتی از یک کلاسی نمونه می سازید آن را تکثیر میکنید ولی روش injection یا تزریق با ان متفاوت است .تعریف دیگری که می توان برای تزریق وابستگی ارائه داد این است که در این روش وابستگی هایی که یک کلاس دارد را به آن تزریق می کنیم به جای اینکه از این وابستگی ها به صورت مستقیم درون کلاس استفاده کنیم .

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

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

روش سوم تزریق اینترفیس است .در این روش ابتدا یک اینترفیس که بیانگر کلاس وابسته است ایجاد می گردد. سپس باید کلاسی که می خواهد از وابسته استفاده کند این اینترفیس را ارث بری کرده و آنرا پیاده سازی نماید.در داخل این اینترفیس باید متدی باشد که کار تزریق وابستگی را انجام دهد .

حال به صورت عملی به بحث تزریق وابستگی ها خواهیم پرداخت

به کلاس زیر دقت کنید

public class Class1
{
   private Class2 objClass2;
   private Class3 objClass3;

    public Class1()
    {
      objClass2 = new Class2();
      objClass3 = new Class3();
    }
   ....
   ....
}

در این کلاس در داخل سازنده آن به نمونه سازی از وابستگی ها اقدام کرده ایم .همان طور که گفته شد این حالت خوب نیست زیرا وابستگی بین کلاس ها زیاد است و اگر تغییری در یک قسمت رخ دهد ناچاریم تغییراتی را در جاهای دیگر برنامه هم  بدهیم .در ادامه دو راه حل خواهیم داشت .اولی این است که که کلاس های 2 و 3 را از یک اینترفیس ارث بری کنیم که این اینترفیس باید قابلیت کلاس مورد نظر را داشته باشد .

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

public class Class1
{
    public Class1(ISomeInterface objClass2, 
                  ISomeOtherInterface objClass3)
    {
      ....
    }
}

در mvc6 به نمونه ای که باید تزریق شود سرویس گفته می شود .این سرویس ها کاری که انجام میدهند این است که کلاس های مورد نظر ما را نمونه سازی کرده و به کنترلر پاس می دهد .و در ضمن چرخه حیات را نیز کنترل می کنند .

سه چرخه حیات در تزریقی که انجام می دهید وجود دارد که به شرح هر کدام می پردازیم

Singleton – اگر قصد داشته باشیم که کلاسی داشته باشیم که در سرتاسر برنامه فقط یک شی از آن وجود داشته باشد به این نوع کلاس Singleton میگویند.

Scoped – به بخشی از برنامه مثلا داخل یک متد کلاس یا بلاک یک scope گفته می شود .در این حالت کلاس مورد نظر در Scope خاصی شناخته شده خواهد بود.

Transient – این نوع از کلاس ها زمانی که به آنها احتیاج باشد ساخته خواهند شد .

Instance – در این حالت باید یک شی از سرویس مورد نظر خود بسازید ، سپس DI از این نمونه به صورت Singleton استفاده می کند .

حال ببینیم هر کدام از این حالت ها چگونه کار می کنند .

یک پروژه mvc ایجاد کنید و در داخل آن یک کلاس و یک اینترفیس مانند زیر ایجاد کنید

public interface IMyServiceInterface
{
    string UniqueID { get; set; }
}

public class MyService:IMyServiceInterface
{
    public string UniqueID { get; set; }

    public MyService()
    {
        this.UniqueID = Guid.NewGuid().ToString();
    }
}

کلاس MyServiceInterface   یک پراپرتی به نام UniqueID دارد .این کلاس اینترفیس IMyServiceInterface را پیاده سازی می کند .این پراپرتی با مقدار GUID پر می شود .این پراپرتی در داخل سازنده کلاس پر می شود .

حال یک کنترلر به نام Home و یک اکشن ایندکس در داخل برنامه ایجاد کنید تا هر کدام از زمان حیات های گفته شده بالا را بررسی کنیم .
Singleton
در داخل کلاس StartUp سرویس DI خود را ثبت می کنیم .در داخل این کلاس کدهای زیر را می نویسیم

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddSingleton<IMyServiceInterface,MyService>();
}


در داخل کنترلر کدهای زیر را می نویسیم

public class HomeController : Controller
{
    private IMyServiceInterface obj;

    public HomeController(IMyServiceInterface obj)
    {
        this.obj = obj;
    }

    public IActionResult Index()
    {
        ViewBag.Obj = this.obj;
        return View();
    }
}


بعد از اجرا شکل زیر را خواهیم دید .

حال اگر یک Browser دیگر باز کنیم و صفحه ایندکس را صد ا بزنیم دقیقا همین GUID را مشاهده خواهیم کرد .
Scoped
کدهای داخل کلاس startup را در خط آخر به صورت زیر تغییر می دهیم

 services.AddScoped<IMyServiceInterface,MyService>();


بعد از اجرای برنامه شکل زیر را خواهید دید

اگر یک مرورگر دیگر بازکنید برای Uniqid دوباره یک شماره جدید ایجاد خواهد شد .

Transient
برای استفاده از این حالت دوباره کدهای داخل startup را به شکل زیر تغییر میدهیم

services.AddTransient<IMyServiceInterface,MyService>();


در داخل کنترلر هم کدهای ما تغییر خواهند کرد .

public class HomeController : Controller
{
    private IMyServiceInterface obj1;
    private IMyServiceInterface obj2;

    public HomeController(IMyServiceInterface obj1, 
                   IMyServiceInterface obj2)
    {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    public IActionResult Index()
    {
        ViewBag.Obj1 = this.obj1;
        ViewBag.Obj2 = this.obj2;
        return View();
    }
}


این بار کنترلر دو عدد از نوع اینترفیس IMyServiceInterface ساخته است .و در داخل اکشن هم دو عدد از uniqid ایجاد خواهد شد

@{
    ViewBag.Title = "Index";
}

<h4>UniqueID of Obj : @ViewBag.Obj1.UniqueID</h4>
<h4>UniqueID of Obj2 : @ViewBag.Obj2.UniqueID</h4>


بعد از اجرای برنامه شکل زیر را خواهید دید

Instance
برای تست این حالت کدهای درون Startup به شکل زیر تغییر می کنند .

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    MyService obj = new MyService();
    obj.UniqueID = "013f98e7-bb8b-4d9f-b5a0-04e930db5357";

    services.AddInstance<IMyServiceInterface>(obj);
}


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

فایل های ضمیمه

ایمان مدائنی

نویسنده 1299 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید