ASP.NET Core DI로 인스턴스 해결 Core MVC 기본

ASP.NET Core MVC 기본 제공 종속성 주입 프레임 워크를 사용하여 형식을 수동으로 확인하려면 어떻게합니까?

컨테이너 설정은 충분히 쉽습니다.

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddTransient<ISomeService, SomeConcreteService>();
}

그러나 ISomeService주입을 수행하지 않고 어떻게 해결할 수 있습니까? 예를 들어, 나는 이것을하고 싶다 :

ISomeService service = services.Resolve<ISomeService>();

에 그러한 방법이 없습니다 IServiceCollection.



답변

IServiceCollection인터페이스에 사용되는 건물 의존성 주입 용기. 완전히 구축 된 후에는 IServiceProvider서비스를 해결하는 데 사용할 수 있는 인스턴스 로 구성됩니다 . IServiceProvider어떤 클래스 에나 주입 할 수 있습니다 . IApplicationBuilderHttpContext 클래스는 통해뿐만 아니라 서비스 제공 업체에 제공 할 수 있습니다 ApplicationServices또는 RequestServices각각 특성.

IServiceProvider 정의 GetService(Type type)서비스를 해결 방법을 .

var service = (IFooService)serviceProvider.GetService(typeof(IFooService));

다음과 같은 몇 가지 편리한 확장 방법이 있습니다. serviceProvider.GetService<IFooService>()( usingfor a 추가)Microsoft.Extensions.DependencyInjection .

시작 클래스 내에서 서비스 해결

의존성 주입

런타임의 호스팅 서비스 제공 업체의 생성자에 특정 서비스를 삽입 할 수 Startup와 같은 클래스 IConfiguration,
IWebHostEnvironment( IHostingEnvironment3.0 이전 버전), ILoggerFactory그리고 IServiceProvider. 후자는 호스팅 계층에 의해 구축 된 인스턴스 이며 응용 프로그램을 시작하는 데 필요한 서비스 만 포함합니다 .

ConfigureServices()방법은 서비스 주입을 허용하지 않으며 IServiceCollection인수 만 허용합니다 . ConfigureServices()응용 프로그램에 필요한 서비스를 등록하는 위치 이기 때문에 이치에 맞습니다 . 그러나 스타트 업의 생성자에 삽입 된 서비스를 여기에서 사용할 수 있습니다 (예 :

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Use Configuration here
}

등록 된 모든 서비스 ConfigureServices()Configure()메소드에 주입 될 수 있습니다 . 이후에 임의의 수의 서비스를 추가 할 수 있습니다.IApplicationBuilder매개 변수 다음에 .

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IFooService>();
}

public void Configure(IApplicationBuilder app, IFooService fooService)
{
    fooService.Bar();
}

종속성 수동 해결

수동 해결 서비스에 필요한 경우, 바람직하게 사용해야 ApplicationServices에서 제공을 IApplicationBuilderConfigure()방법 :

public void Configure(IApplicationBuilder app)
{
    var serviceProvider = app.ApplicationServices;
    var hostingEnv = serviceProvider.GetService<IHostingEnvironment>();
}

클래스 IServiceProvider의 생성자에서 in 을 전달하고 직접 사용할 수 Startup있지만 위와 같이 제한된 서비스 하위 집합이 포함 되므로 유틸리티가 제한적입니다.

public Startup(IServiceProvider serviceProvider)
{
    var hostingEnv = serviceProvider.GetService<IWebHostEnvironment>();
}

ConfigureServices()분석법 에서 서비스를 해결해야하는 경우 다른 접근 방식이 필요합니다. 해당 시점까지 등록 된 서비스를 포함 IServiceProvider하는 IServiceCollection인스턴스 에서 중간체 를 빌드 할 수 있습니다 .

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IFooService, FooService>();

    // Build the intermediate service provider
    var sp = services.BuildServiceProvider();

    // This will succeed.
    var fooService = sp.GetService<IFooService>();
    // This will fail (return null), as IBarService hasn't been registered yet.
    var barService = sp.GetService<IBarService>();
}

참고 :
일반적으로 ConfigureServices()메소드 내에서 서비스를 분석하지 마십시오. 실제로 애플리케이션 서비스를 구성 하는 위치 입니다. 때로는 IOptions<MyOptions>인스턴스에 액세스해야 합니다. IConfiguration인스턴스 의 값을 인스턴스 에 바인딩하여이를 수행 할 수 있습니다 MyOptions(기본적으로 옵션 프레임 워크의 기능 임).

public void ConfigureServices(IServiceCollection services)
{
    var myOptions = new MyOptions();
    Configuration.GetSection("SomeSection").Bind(myOptions);
}

서비스 수동 해결 (일명 서비스 로케이터)은 일반적으로 안티 패턴으로 간주됩니다 . 프레임 워크 및 / 또는 인프라 계층에 대한 사용 사례가 있지만 가능한 많이 사용하지 않아야합니다.


답변

인스턴스를 수동으로 해결하려면 IServiceProvider 인터페이스를 .

Startup.ConfigureServices에서 종속성 해결

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    var serviceProvider = services.BuildServiceProvider();
    var service = serviceProvider.GetService<IMyService>();
}

시작시 종속성 해결

public void Configure(
    IApplicationBuilder application,
    IServiceProvider serviceProvider)
{
    // By type.
    var service1 = (MyService)serviceProvider.GetService(typeof(MyService));

    // Using extension method.
    var service2 = serviceProvider.GetService<MyService>();

    // ...
}

ASP.NET Core 3에서 Startup.Configure의 종속성 해결

public void Configure(
    IApplicationBuilder application,
    IWebHostEnvironment webHostEnvironment)
{
    app.ApplicationServices.GetService<MyService>();
}

런타임 주입 서비스 사용

일부 유형은 메소드 매개 변수로 주입 될 수 있습니다.

public class Startup
{
    public Startup(
        IHostingEnvironment hostingEnvironment,
        ILoggerFactory loggerFactory)
    {
    }

    public void ConfigureServices(
        IServiceCollection services)
    {
    }

    public void Configure(
        IApplicationBuilder application,
        IHostingEnvironment hostingEnvironment,
        IServiceProvider serviceProvider,
        ILoggerFactory loggerfactory,
        IApplicationLifetime applicationLifetime)
    {
    }
}

컨트롤러 작업에서 종속성 해결

[HttpGet("/some-action")]
public string SomeAction([FromServices] IMyService myService) => "Hello";


답변

템플릿으로 응용 프로그램을 생성하면 Startup클래스 에 다음과 같은 내용이 표시됩니다 .

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddMvc();
}

그런 다음 여기에 종속성을 추가 할 수 있습니다.

services.AddTransient<ITestService, TestService>();

ITestService컨트롤러 에서 액세스 IServiceProvider하려면 생성자를 추가하면 주입됩니다.

public HomeController(IServiceProvider serviceProvider)

그런 다음 추가 한 서비스를 해결할 수 있습니다.

var service = serviceProvider.GetService<ITestService>();

일반 버전을 사용하려면 확장명이 포함 된 네임 스페이스를 포함해야합니다.

using Microsoft.Extensions.DependencyInjection;

ITestService.cs

public interface ITestService
{
    int GenerateRandom();
}

TestService.cs

public class TestService : ITestService
{
    public int GenerateRandom()
    {
        return 4;
    }
}

Startup.cs (ConfigureServices)

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

    services.AddTransient<ITestService, TestService>();
}

HomeController.cs

using Microsoft.Extensions.DependencyInjection;

namespace Core.Controllers
{
    public class HomeController : Controller
    {
        public HomeController(IServiceProvider serviceProvider)
        {
            var service = serviceProvider.GetService<ITestService>();
            int rnd = service.GenerateRandom();
        }


답변

등록중인 다른 종속성의 생성자에게 전달하기 위해 하나의 종속성을 해결해야하는 경우이를 수행 할 수 있습니다.

문자열과 ISomeService를받은 서비스가 있다고 가정 해 봅시다.

public class AnotherService : IAnotherService
{
    public AnotherService(ISomeService someService, string serviceUrl)
    {
        ...
    }
}

Startup.cs에 등록하려면 다음을 수행해야합니다.

services.AddScoped<IAnotherService>(ctx =>
      new AnotherService(ctx.GetService<ISomeService>(), "https://someservice.com/")
);


답변

이런 방식으로 AuthorizeAttribute와 같은 속성에 종속성을 삽입 할 수 있습니다.

var someservice = (ISomeService)context.HttpContext.RequestServices.GetService(typeof(ISomeService));


답변

나는 이것이 오래된 질문이라는 것을 알고 있지만 오히려 명백하고 역겨운 해킹이 여기에 없다는 것에 놀랐습니다.

정의한대로 서비스에서 필요한 값을 가져 오기 위해 고유 한 ctor 함수를 정의 할 수있는 기능을 활용할 수 있습니다. 분명히 명시 적으로 정의를 제거 / 삭제하고 다시 추가하지 않는 한 서비스가 요청 될 때마다 실행됩니다. 이 서비스 는 악용 ctor의 첫 번째 구성 내에서 수행 됩니다.

이 방법은 서비스 구성 중에 서비스 트리를 구축하거나 사용하지 않아도되는 이점이 있습니다. 여전히 서비스 구성 방법을 정의하고 있습니다.

public void ConfigureServices(IServiceCollection services)
{
    //Prey this doesn't get GC'd or promote to a static class var
    string? somevalue = null;

    services.AddSingleton<IServiceINeedToUse, ServiceINeedToUse>(scope => {
         //create service you need
         var service = new ServiceINeedToUse(scope.GetService<IDependantService>())
         //get the values you need
         somevalue = somevalue ?? service.MyDirtyHack();
         //return the instance
         return service;
    });
    services.AddTransient<IOtherService, OtherService>(scope => {
         //Explicitly ensuring the ctor function above is called, and also showcasing why this is an anti-pattern.
         scope.GetService<IServiceINeedToUse>();
         //TODO: Clean up both the IServiceINeedToUse and IOtherService configuration here, then somehow rebuild the service tree.
         //Wow!
         return new OtherService(somevalue);
    });
}

이 패턴을 수정하는 방법은 에 대한 암시 적으로 또는 메서드의 반환 값에 의존하지 않고 다른 방식으로 해당 종속성을 명시 적으로 해결하는 것이 아니라 OtherService에 대한 명시 적 종속성 을 제공 하는 IServiceINeedToUse것입니다.


답변

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

    services.AddDbContext<ConfigurationRepository>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("SqlConnectionString")));

    services.AddScoped<IConfigurationBL, ConfigurationBL>();
    services.AddScoped<IConfigurationRepository, ConfigurationRepository>();
}