como usar dependência com escopo em um singleton em c # / ASP

Eu sou novo em C # / ASP vindo de um mundo Java. Eu li este artigo: https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options que sabiamente avisa sobre os perigos associados a injetar uma dependência com um escopo menor. Infelizmente isso não explica como resolver esse problema em C # / ASP. Em Java há um conceito de provedor

interface Provider { T get(); } 

que, entre outras coisas, ajuda a resolver o problema de escopo: sempre que uma binding para algum tipo T é registrada, podemos injetar uma instância gerada automaticamente de Provider em vez de T e depois obter uma instância de T sempre que for necessário: O Provedor gerado garante que obtenhamos uma instância apropriada para o escopo atual (qualquer que seja esse escopo: solicitação HTTP, session HTTP ou outros escopos personalizados). A estrutura DI padrão embutida no núcleo do ASP.NET não tem nada parecido com isso, mas eu pensei em C # que deveria ser muito fácil de implementar, já que os genéricos em C # não são como o do java ( https://docs.oracle.com/ javase / tutorial / java / generics / erasure.html ). Então eu criei a seguinte class:

 public class Provider: IProvider { private readonly IServiceProvider serviceProvider; public Provider(IServiceProvider serviceProvider) { this.serviceProvider = serviceProvider; } public T IProvider.Get() { return serviceProvider.GetService(); } } 

e eu tentei usá-lo da seguinte maneira:

 public class SingletonService : ISingletonService { private readonly IProvider scopedServiceProvider; public SingletonService(IProvider scopedServiceProvider) { this.scopedServiceProvider = scopedServiceProvider; } public string PerformMyTask() { var scopedDependency = scopedServiceProvider.Get(); // do something with scopedDependency to verify we get instances // appropriate for the current scope } } 

e na minha class de boot:

 public void ConfigureServices(IServiceCollection services) { services.AddSingleton(); services.AddScoped(); services.AddTransient<IProvider, Provider>(); // other bindings here } 

Infelizmente isso não funciona da maneira que eu pretendia como IServiceProvider instância parece ser também escopo para o atual pedido HTTP e recebo exatamente a mesma instância de ScopedDependency do meu provedor durante o processamento de diferentes solicitações 🙁

Alguma dica de como posso resolver esse problema? Existe algum object de “nível mais alto” do que o ServiceProvider, ligado aproximadamente ao ciclo de vida do aplicativo (não à solicitação atual) que cria instâncias de objects com escopo de solicitação (ou do próprio ServiceProvider) que posso injetar em meus objects Provider em vez de ServiceProvider? Por exemplo, em Java, se eu usar o google Guice como uma estrutura de DI, há um object Injector, geralmente criado na boot de um aplicativo que contém todas as ligações de tipos e tem um método

  T getInstance(Class type); 

que verifica qual é o escopo atual e retorna uma instância correspondente.

editar:

Eu acho que uma maneira possível de fazer isso seria obter uma nova referência à instância de ServiceProvider cada vez no método Proivder .Get () em vez de injetar no construtor e armazenar como uma instância var. Dessa forma, meus componentes ainda não seriam poluídos com uma referência ao IServiceProvider específico do framework, já que eles seriam ocultados na implementação do Provider que eles acessam por meio da interface abstrata IProvider . No entanto, não consigo encontrar na web se é possível obter essa referência da minha class Provider e como fazer isso. Qualquer ponte nesta direção seria apreciada 🙂

Obrigado!

ok, achei:

 public class Provider : IProvider { IHttpContextAccessor contextAccessor; public Provider(IHttpContextAccessor contextAccessor) { this.contextAccessor = contextAccessor; } T IProvider.Get() { return contextAccessor.HttpContext.RequestServices.GetService(); } } 

e na boot:

  public void ConfigureServices(IServiceCollection services) { services.TryAddSingleton(); services.AddSingleton(); services.AddScoped(); services.AddTransient, Provider>(); // other bindings } 

🙂

consulte https://github.com/aspnet/Hosting/issues/793 para obter mais detalhes sobre como usar e registrar o HttpContextAccessor