컨트롤러에 대 한 종속성 주입Dependency injection into controllers

<a name=dependency-injection-controllers>

으로 Steve SmithBy Steve Smith

ASP.NET Core MVC 컨트롤러의 생성자를 통해 명시적으로 해당 종속성을 요청 해야 합니다.ASP.NET Core MVC controllers should request their dependencies explicitly via their constructors. 경우에 따라 개별 컨트롤러 작업에는 서비스 필요할 수 있습니다 및 컨트롤러 수준에서 요청할 수 있는 적합할 수 있습니다.In some instances, individual controller actions may require a service, and it may not make sense to request at the controller level. 이 경우에 작업 메서드의 매개 변수로 서비스를 삽입할 수도 있습니다.In this case, you can also choose to inject a service as a parameter on the action method.

보거나 다운로드 샘플 코드 (다운로드 하는 방법을)View or download sample code (how to download)

종속성 주입Dependency Injection

종속성 주입은 뒤에 기술에서 종속성 반전 원칙, 응용 프로그램을 느슨하게 결합 된 모듈의 구성 될 수 있도록 합니다.Dependency injection is a technique that follows the Dependency Inversion Principle, allowing for applications to be composed of loosely coupled modules. ASP.NET Core에서 기본적으로 지원 종속성 주입, 그러면 응용 프로그램 쉽게 테스트 하 고 유지 관리 합니다.ASP.NET Core has built-in support for dependency injection, which makes applications easier to test and maintain.

생성자 삽입Constructor Injection

ASP.NET Core 기본적으로 지원 되는 생성자 기반 종속성 주입 MVC 컨트롤러까지 확장 됩니다.ASP.NET Core's built-in support for constructor-based dependency injection extends to MVC controllers. 서비스 종류를 생성자 매개 변수로 컨트롤러에 단순히 추가 ASP.NET Core 서비스 컨테이너에서 기본 제공를 사용 하 여 해당 형식을 확인 하려고 합니다.By simply adding a service type to your controller as a constructor parameter, ASP.NET Core will attempt to resolve that type using its built in service container. 서비스는 항상 그렇지는 않지만 일반적으로 정의 된 인터페이스를 사용 하 여 합니다.Services are typically, but not always, defined using interfaces. 예를 들어 현재 시간에 따라 달라 지는 비즈니스 논리가 응용 프로그램에 설정된 된 시간을 사용 하는 구현에 전달 하 여 테스트 수 있는 시간 (아니라 하드 코딩 것)를 검색 하는 서비스를 삽입할 수 있습니다.For example, if your application has business logic that depends on the current time, you can inject a service that retrieves the time (rather than hard-coding it), which would allow your tests to pass in implementations that use a set time.

using System;

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

런타임 시 시스템 클록을 사용 하는 이와 같은 인터페이스를 구현 하는 매우 간단 합니다.Implementing an interface like this one so that it uses the system clock at runtime is trivial:

using System;
using ControllerDI.Interfaces;

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

이 위치에 컨트롤러에서 서비스를 사용할 수 있습니다.With this in place, we can use the service in our controller. 이 경우 일부 논리를 추가 했습니다는 HomeController Index 하루 중 시간을 기반으로 하는 메서드를 사용자에 게 인사말을 표시 합니다.In this case, we have added some logic to the HomeController Index method to display a greeting to the user based on the time of day.

using ControllerDI.Interfaces;
using Microsoft.AspNetCore.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();
        }
    }
}

지금 응용 프로그램을 실행 하는 경우 오류를 발생 가능성이 합니다.If we run the application now, we will most likely encounter an error:

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 클래스입니다.This error occurs when we have not configured a service in the ConfigureServices method in our Startup class. 에 대 한 요청을 지정 하려면 IDateTime 의 인스턴스를 사용 하 여 해결 해야 SystemDateTime, 하려면 아래 목록에서 강조 표시 된 줄을 추가 하면 ConfigureServices 메서드:To specify that requests for IDateTime should be resolved using an instance of SystemDateTime, add the highlighted line in the listing below to your ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    // Add application services.
    services.AddTransient<IDateTime, SystemDateTime>();
}

참고

여러 가지 다른 수명 옵션 중 하나를 사용 하 여이 특정 서비스를 구현할 수 있습니다 (Transient, Scoped, 또는 Singleton).This particular service could be implemented using any of several different lifetime options (Transient, Scoped, or Singleton). 참조 종속성 주입 각 범위 옵션 내용이 미치는 영향을 서비스의 동작을 이해할 수 있습니다.See Dependency Injection to understand how each of these scope options will affect the behavior of your service.

서비스를 구성한 후 응용 프로그램을 실행 하 고 홈 페이지로 이동 해야 시간 기반 메시지 예상 대로 표시 합니다.Once the service has been configured, running the application and navigating to the home page should display the time-based message as expected:

서버 인사말

참조 테스트 컨트롤러 논리 종속성을 명시적으로 요청 하는 방법에 알아보려면 http://deviq.com/explicit-dependencies-principle/ 컨트롤러에 더 쉽게 코드를 테스트 합니다.See Testing Controller Logic to learn how to explicitly request dependencies http://deviq.com/explicit-dependencies-principle/ in controllers makes code easier to test.

ASP.NET Core 기본 종속성 주입 서비스를 요청 하는 클래스에 대 한 단일 생성자만 지원 합니다.ASP.NET Core's built-in dependency injection supports having only a single constructor for classes requesting services. 둘 이상의 생성자가 있으면 내용의 된 예외가 나타날 수 있습니다.If you have more than one constructor, you may get an exception stating:

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)

오류 메시지에서 설명 하는 대로 단일 생성자만이 문제를 해결할 수 있습니다.As the error message states, you can correct this problem having just a single constructor. 수도 있습니다 제 3 자 구현은 기본 종속성 주입 지원 바꿉니다지 원하는 여러 명의 생성자의 여러 합니다.You can also replace the default dependency injection support with a third party implementation, many of which support multiple constructors.

FromServices와 삽입 작업Action Injection with FromServices

경우에 따라 컨트롤러 내에서 둘 이상의 작업에 대 한 서비스를 필요 하지 않습니다.Sometimes you don't need a service for more than one action within your controller. 이 경우 서비스 작업 메서드에 매개 변수로 삽입을 하면 됩니다.In this case, it may make sense to inject the service as a parameter to the action method. 특성으로 매개 변수를 표시 하 여 이렇게 [FromServices] 다음과 같이 합니다.This is done by marking the parameter with the attribute [FromServices] as shown here:

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

    return View();
}

컨트롤러에서 설정 액세스Accessing Settings from a Controller

컨트롤러 내에서 응용 프로그램 또는 구성 설정에 액세스 하는 일반적인 패턴입니다.Accessing application or configuration settings from within a controller is a common pattern. 이 액세스에 설명 된 옵션 패턴을 사용 해야 구성합니다.This access should use the Options pattern described in configuration. 일반적으로 하지 요청 해야 설정을 종속성 주입을 사용 하 여 컨트롤러에서 직접 합니다.You generally should not request settings directly from your controller using dependency injection. 하는 것이 좋습니다 요청은 IOptions<T> 인스턴스, 여기서 T 필요한 구성 클래스입니다.A better approach is to request an IOptions<T> instance, where T is the configuration class you need.

옵션 패턴을 사용 하려면이 다음과 같은 옵션을 나타내는 클래스를 만들려면:To work with the options pattern, you need to create a class that represents the options, such as this one:

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

옵션 모델을 사용 하며 구성 클래스는 서비스 컬렉션에 추가 응용 프로그램을 구성 해야 ConfigureServices:Then you need to configure the application to use the options model and add your configuration class to the services collection in ConfigureServices:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .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>();
}

참고

위의 목록에서 구성 하는 중 응용 프로그램을 JSON 형식 파일에서 설정을 읽습니다.In the above listing, we are configuring the application to read the settings from a JSON-formatted file. 또한 위의 주석 처리 된 코드에 표시 된 대로 코드에서 완전히 설정을 구성할 수 있습니다.You can also configure the settings entirely in code, as is shown in the commented code above. 참조 구성 추가 구성 옵션에 대 한 합니다.See Configuration for further configuration options.

강력한 형식의 구성 개체를 지정한 후 (이 경우 SampleWebSettings) 추가할 서비스 컬렉션 요청할 수 있습니다이 모든 컨트롤러 또는 동작 메서드에서 인스턴스를 요청 하 여 IOptions<T> (이 경우 IOptions<SampleWebSettings>) .Once you've specified a strongly-typed configuration object (in this case, SampleWebSettings) and added it to the services collection, you can request it from any Controller or Action method by requesting an instance of IOptions<T> (in this case, IOptions<SampleWebSettings>). 다음 코드는 컨트롤러에서 설정을 요청 것 하나를 보여 줍니다.The following code shows how one would request the settings from a controller:

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();
    }
}

옵션 패턴을 설정 및 구성을 서로 분리할 수를 허용 하 고 되도록 컨트롤러 팔 로우 문제의 분리방법 또는 위치를 알 필요가 없으므로 않아야 하므로 설정을 찾을 수 정보입니다.Following the Options pattern allows settings and configuration to be decoupled from one another, and ensures the controller is following separation of concerns, since it doesn't need to know how or where to find the settings information. 하기가 컨트롤러 단위 테스트를 쉽게 테스트 컨트롤러 논리있기 때문에, 없습니다 정적 문에 붙이는 또는 컨트롤러 클래스 내에서 설정 클래스의 직접 인스턴스화입니다.It also makes the controller easier to unit test Testing Controller Logic, since there is no static cling or direct instantiation of settings classes within the controller class.