3

Я хочу добавить службу на основе значения заголовка HTTP. Поэтому у меня есть 2 класса - DbDataProvider и InMemDataProvider, оба из которых реализованы из IDataProvider. Всякий раз, когда выполняется вызов API, клиенту передается заголовок, который определяет, требуется ли DbDataProvider или требуется InMemDataProvider. Как мне это достичь? Короче говоря, мне нужно внедрить услугу в ServiceCollection в одном из посредников. Это возможно?Injecting Service в промежуточном программном обеспечении в ASP.NET Core

Проблема в том, что в методе ConfigureService в классе Startup я не могу получить HttpContext. Я написал промежуточное программное обеспечение, в котором я могу получить HTTP-контекст, но как мне добавить службу там?

+1

Предотвратите инъекционные услуги на основе условий выполнения, так же, как вы должны [не вводить сами данные времени выполнения] (https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99) в ваше приложение компоненты. Вместо этого есть составной компонент, который вводится с обеих реализаций и перенаправляет вызов реализации во время выполнения. Заводы вряд ли когда-либо будут правильным решением. – Steven

ответ

4

Существует нет простого или чистого способа сделать это. Вы не можете изменить IServiceCollection вне метода ConfigureServices. Но даже если бы это было возможно, это бесполезно, потому что контейнер уже был создан до вызова Configure.

Что вы можете сделать, это создать заводский класс и зарегистрировать его как область действия.

public interface IDataProviderFactory 
{ 
    bool UseInMemoryProvider { get; set; } 
    IDataProvider Create(); 
} 

public class DataProviderFactory : IDataProviderFactory 
{ 
    private readonly IServiceProvider provider; 

    public bool UseInMemoryProvider { get; set; } 

    public DataProviderFactory(IServiceProvider provider) 
    { 
     this.provider = provider; 
    } 

    public IDataProvider Create() 
    { 
     if(UseInMemoryProvider) 
     { 
      return provider.RequestService<InMemoryDataProvider>(); 
     } 

     return provider.RequestService<DbDataProvider>(); 
    } 
} 

Затем в ПО промежуточного слоя:

public class MyMiddleware 
{ 
    public void Invoke(HttpContext context) 
    { 
     var dataProviderFactory = context.RequestServices.RequestService<IDataProviderFactory>(); 

     // Your logic here 
     if(...) 
     { 
      dataProviderFactory.UseInMemoryStore = true; 
     } 
    } 
} 

и в контроллере/услуги:

public class MyController : Controller 
{ 
    private readonly IDataProvider dataProvider; 

    public MyController(IDataProviderFactory dataProviderFactory) 
    { 
     dataProvider = dataProviderFactory.Create(); 
    } 
} 
1

Ответ выше от Тзена правильно. Вы должны внедрить завод.

Но кроме того, вы также можете зарегистрировать заводские методы для коллекции услуг. Например:

Services.AddTransient(serviceProvider => serviceProvider.GetService<IDataProviderFactory>().Create()) 

Это зарегистрирует ваш IDataProvider. В Create вы должны оценить это значение заголовка HTTP, чтобы он вернул правильный экземпляр IDataProvider. Затем в любом классе, в котором вы нуждаетесь, вы можете просто запросить IDataProvider через конструктор, и правильная реализация будет предоставлена ​​контейнером.

+0

Это приведет к тому, что всегда будет возвращен один и тот же экземпляр, поскольку он является одиночным;) Еще одна проблема с ним в этом месте заключается в том, что у вас нет прямого доступа к «HttpContext», а «IHttpContextAccessor» по умолчанию не зарегистрирован (по соображениям производительности), так как создание его по каждому запросу имеет небольшие накладные расходы, и если его другим службам не требуется HttpContext вне контроллера, он не должен просто регистрировать его по одной единственной причине. https://github.com/aspnet/Announcements/issues/190 – Tseng

+0

Я имею в виду переходный (обновленный ответ). И мы действительно зарегистрировали IHttpContextAccessor. Каждому из них я бы сказал. –

Смежные вопросы