ASP.NET Core MVC 기본 제공 종속성 주입 프레임 워크를 사용하여 형식을 수동으로 확인하려면 어떻게합니까?
컨테이너 설정은 충분히 쉽습니다.
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddTransient<ISomeService, SomeConcreteService>();
}
그러나 ISomeService
주입을 수행하지 않고 어떻게 해결할 수 있습니까? 예를 들어, 나는 이것을하고 싶다 :
ISomeService service = services.Resolve<ISomeService>();
에 그러한 방법이 없습니다 IServiceCollection
.
답변
IServiceCollection
인터페이스에 사용되는 건물 의존성 주입 용기. 완전히 구축 된 후에는 IServiceProvider
서비스를 해결하는 데 사용할 수 있는 인스턴스 로 구성됩니다 . IServiceProvider
어떤 클래스 에나 주입 할 수 있습니다 . IApplicationBuilder
및HttpContext
클래스는 통해뿐만 아니라 서비스 제공 업체에 제공 할 수 있습니다 ApplicationServices
또는 RequestServices
각각 특성.
IServiceProvider
정의 GetService(Type type)
서비스를 해결 방법을 .
var service = (IFooService)serviceProvider.GetService(typeof(IFooService));
다음과 같은 몇 가지 편리한 확장 방법이 있습니다. serviceProvider.GetService<IFooService>()
( using
for a 추가)Microsoft.Extensions.DependencyInjection
.
시작 클래스 내에서 서비스 해결
의존성 주입
런타임의 호스팅 서비스 제공 업체의 생성자에 특정 서비스를 삽입 할 수 Startup
와 같은 클래스 IConfiguration
,
IWebHostEnvironment
( IHostingEnvironment
3.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
에서 제공을 IApplicationBuilder
에 Configure()
방법 :
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>();
}