Внедрение службы в промежуточном программном обеспечении в ASP.NET Core
Я хочу внедрить сервис на основе значения заголовка HTTP. Итак, у меня есть 2 класса - DbDataProvider и InMemDataProvider, оба реализованы из IDataProvider. Всякий раз, когда выполняется вызов API, клиент передает заголовок, который определяет, требуется ли DbDataProvider или InMemDataProvider. Как мне этого добиться? Короче говоря, мне нужно внедрить службу в ServiceCollection в одном из промежуточных программ. Это возможно?
Проблема в том, что в методе ConfigureService в классе запуска я не могу получить HttpContext. Я написал промежуточное программное обеспечение, с помощью которого я могу получить HTTP-контекст, но как я могу внедрить там Службу?
2 ответа
Нет простого или чистого способа сделать это. Вы не можете изменить 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();
}
}
Вы можете добиться этого в вашей конфигурации DI в Startup.cs
,
Они ключ services.AddHttpContextAccessor()
который позволяет получить доступ к HttpContext.
services.AddHttpContextAccessor();
services.AddScoped<DbDataProvider>();
services.AddScoped<InMemDataProvider>();
services.AddScoped<IDataProvider>(ctx =>
{
var contextAccessor = ctx.GetService<IHttpContextAccessor>();
var httpContext = contextAccessor.HttpContext;
// Whatever the header is that you are looking for
if (httpContext.Request.Headers.TryGetValue("Synthetic", out var syth))
{
return ctx.GetService<InMemDataProvider>();
}
else
{
return ctx.GetService<DbDataProvider>();
}
});
Ответ выше от Цена правильный. Вы должны реализовать фабрику.
Но кроме того, вы также можете зарегистрировать фабричные методы в коллекции сервисов. Вот так:
Services.AddTransient(serviceProvider => serviceProvider.GetService<IDataProviderFactory>().Create())
Это регистрирует ваш IDataProvider. В Create вы должны оценить это значение HTTP-заголовка, чтобы он возвращал правильный экземпляр IDataProvider. Тогда в любом нужном вам классе вы можете просто запросить IDataProvider через конструктор, и контейнер предоставит правильную реализацию.