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

یکشنبه 26 اردیبهشت 1395

در این مقاله تزریق وابستگی ، تزریق سازنده ها ، عمل تزریق با FromServices و دسترسی به تنظیمات از یک کنترلر را مورد بررسی قرار خواهیم داد .

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

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

Dependency Injection یا تزریق وابستگی

تزریق وابستگی یک تکنیک است که از اصل وارونگی وابستگی تبعیت میکند و به برنامه اجازه می دهد که ترکیبی از ماژولهای آزاد باشد . ASP.net 5 از تزریق وابستگی پشتیبانی میکند و انتظار می رود که این تکنیک در برنامه ها بجای نمونه سازیهای مستقیم و دسترسیهای استاتیک استفاده شود.

 Constructor Injection یا تزریق سازنده ها

built-in در ASP.net 5 از گسترش تزریق وابستگی های بر اساس سازنده (Constructor-based) برای  ASP.net MVC 6 پشتیبانی میکند. با اضافه کردن یک نوع سرویس به کنترلرها به عنوان یک پارامتر سازنده ،  ASP.net  قادر خواهد بود مشکل این نوع را با استفاده از سرویس  built-in خود حل کند. سرویسها معمولا اما نه همیشه بوسیله رابط ها یا  Interface ها تعریف می شوند. برای مثال اگر برنامه شما دارای منطق کاری باشد که به زمان جاری وابسته است، می توان یک سرویس که زمان را بازیابی میکند تزریق کنید. برای مثال کد زیر را در نظر بگیرید.

using System;

namespace ControllerDI.Interfaces
{
    public interface IDateTime
    {
        DateTime Now { get; }
    }
}

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

using System;
using ControllerDI.Interfaces;

namespace ControllerDI.Services
{
    public class SystemDateTime : IDateTime
    {
        public DateTime Now
        {
            get { return DateTime.Now; }
        }
    }
}

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

using ControllerDI.Interfaces;
using Microsoft.AspNet.Mvc;

namespace ControllerDI.Controllers
{
    public class HomeController : Controller
    {
        private readonly IDateTime _dateTime;

        public HomeController(IDateTime dateTime)
        {
            _dateTime = dateTime;
        }

        public IActionResult Index()
        {
            var serverTime = _dateTime.Now;
            if (serverTime.Hour < 12)
            {
                ViewData["Message"] = "It's morning here - Good Morning!";
            }
            else if (serverTime.Hour < 17)
            {
                ViewData["Message"] = "It's afternoon here - Good Afternoon!";
            }
            else
            {
                ViewData["Message"] = "It's evening here - Good Evening!";
            }
            return View();
        }
    }
}

در اینجا اگر برنامه اجرا شود با خطا مواجه خواهیم شد:

An unhandled exception occurred while processing the request.

InvalidOperationException: Unable to resolve service for type ‘ControllerDI.Interfaces.IDateTime’ while attempting to activate ‘ControllerDI.Controllers.HomeController’. Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)

این خطا هنگامی رخ می دهد که سرویس خود را در متد ConfigureServices  در کلاس  Startup پیکربندی نکرده باشیم.

مشخص است که درخواست برای  IDateTime باید با استفاده از یک نمونه SystemDateTime حل شود. بنابراین باید در متد  ConfigureServices خط مشخص شده در زیر را اضافه کرد.

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

    // Add application services.
    services.AddTransient<IDateTime, SystemDateTime>();
}

هنگامی که سرویس پیکربندی شد می توان برنامه را اجرا کرد . در صفحه  Home  پیامی بر اساس زمان نمایش داده خواهد شد.

تزریق وابستگی built-in  در ASP.net 5 یک سازنده واحد برای کلاسهای سرویس های درخواستی دارد. اگر بیشتر از یک سازنده داشته باشید ممکن است با یک حطا استثناء مواجه شوید.

An unhandled exception occurred while processing the request.

InvalidOperationException: Multiple constructors accepting all given argument types have been found in type ‘ControllerDI.Controllers.HomeController’. There should only be one applicable constructor. Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type instanceType, Type[] argumentTypes, ConstructorInfo& matchingConstructor, Nullable`1[]& parameterMap)

عنوان پیام نشان می دهد که این مشکل با داشتن فقط یک سازنده حل می شود. همچنین می توان پشتیبانی تزریق وابستگی پیش فرض را با یک پیاده سازی دیگر عوض کرد که که از چند سازنده همزمان پشتیبانی کند.

عمل تزریق با  FromServices

 گاهی اوقات برای تعداد بیشتر از یک  action در کنترلر ، نیازی به یک سرویس نیست. در این حالت می توان برای تزریق یک سرویس ، آن را به صورت یک پارامتر به متد  action ارسال کرد. این کار با استفاده از قرار دادن صفت [FromServices] برای پارامتر انجام می شود. مثالی در زیر آورده شده است.

public IActionResult About([FromServices] IDateTime dateTime)
{
    ViewData["Message"] = "Currently on the server the time is " + dateTime.Now;

    return View();
}

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

دسترسی به نرم افزار یا پیکربندی تنظیمات از یک کنترلر الگویی مشترک است. این دسترسی از گزینه های تعریف شده الگو در  configuration استفاده میکند. عموما تنظیمات را نباید مستقیما از کنترلر با استفاده از تزریق وابستگی درخواست کرد. روش بهتر ، درخواست یک نمونه< IOption<T است، که T همان کلاس پیکربندی است که نیاز دارید.

برای کار با الگوی گزینه ها ، به ایجاد یک کلاس  که گزینه ها را ارائه می دهد نیاز دارید ، مانند مثال زیر :

namespace ControllerDI.Model
{
    public class SampleWebSettings
    {
        public string Title { get; set; }
        public int Updates { get; set; }
    }
}

سپس به پیکربندی برنامه برای استفاده از مدل گزینه ها و اضافه کردن کلاس پیکربندی به مجموعه سرویسها در  ConfigureServices نیاز است.

public Startup()
{
    var builder = new ConfigurationBuilder()
        .AddJsonFile("samplewebsettings.json");
    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; set; }

// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
    // Required to use the Options<T> pattern
    services.AddOptions();

    // Add settings from configuration
    services.Configure<SampleWebSettings>(Configuration);

    // Uncomment to add settings from code
    //services.Configure<SampleWebSettings>(settings =>
    //{
    //    settings.Updates = 17;
    //});

    services.AddMvc();

    // Add application services.
    services.AddTransient<IDateTime, SystemDateTime>();
}

هنگامی که یک شیء پیکربندی را به صورت  Strongly-typed مشخص میکنید(در این مثال  SampleWebSettings) و آن را به مجموعه سرویس ها اضافه میکنید ، می توانید آن را از هر کنترلر یا  action درخواست کنید که  یک از نمونه ای از  IOption<T> در خواست می شود. در زیر نشان داده شده است که چگونه تنظیمات از طریق کنترلر درخواست می شوند.

 public class SettingsController : Controller
    {
        private readonly SampleWebSettings _settings;

        public SettingsController(IOptions<SampleWebSettings> settingsOptions )
        {
            _settings = settingsOptions.Value;
        }

        public IActionResult Index()
        {
            ViewData["Title"] = _settings.Title;
            ViewData["Updates"] = _settings.Updates;
            return View();
        }
    }

الگوی گزینه ها اجازه می دهد که تنظیمات و پیکربندی از یکدیگر جدا باشند و اطمینان می دهد که کنترلر جدا از نگرانی هاست و لازم نیست بداند که اطلاعات تنظیمات از کجا یافت می شوند. همچنین کنترلر را آسان تر می توان با استفاده از  unit test  تست کرد زیرا در آن فراخوانی های استاتیک یا نمونه سازیهای مستقیم از کلاسهای تنظیمات وجود ندارد .

آموزش asp.net mvc

برنامه نویسان

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

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

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