9

我想基于 HTTP 标头值注入服务。所以我有 2 个类 - DbDataProvider 和 InMemDataProvider,它们都是从 IDataProvider 实现的。每当进行 API 调用时,客户端都会传递一个标头,以确定是需要 DbDataProvider 还是需要 InMemDataProvider。我该如何做到这一点?所以简而言之,我需要在其中一个中间件的 ServiceCollection 中注入服务。那可能吗?

问题是在 Startup 类的 ConfigureService 方法中我无法获取 HttpContext。我已经编写了一个中间件,使用它可以获取 HTTP 上下文,但是如何在那里注入服务?

4

3 回答 3

10

没有简单或干净的方法可以做到这一点。您不能修改方法的IServiceCollection外部ConfigureServicesConfigure但即使你可以,它也没有用,因为容器在被调用之前已经构建。

您可以做的是创建一个工厂类并将其注册为作用域。

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();
    }
}
于 2016-07-04T00:33:57.567 回答
3

你可以在你的 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>();
    }
});
于 2019-04-17T09:54:28.893 回答
2

上面Tsen的回答是正确的。你应该实现一个工厂。

但除此之外,您还可以将工厂方法注册到服务集合。像这样:

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

这将注册您的 IDataProvider。在 Create 中,您应该评估该 HTTP 标头值,以便它返回正确的 IDataProvider 实例。然后在您需要的任何类中,您只需通过构造函数请求 IDataProvider ,容器将提供正确的实现。

于 2016-07-04T06:30:57.943 回答