在ASP.NET核心中注入中间件服务

时间:2016-07-03 21:26:07

标签: dependency-injection asp.net-core asp.net-core-middleware

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

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

3 个答案:

答案 0 :(得分:6)

没有简单或干净的方法来做到这一点。您无法修改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 :(得分:2)

Tsen的上述答案是正确的。你应该实施一个工厂。

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

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

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

答案 2 :(得分:0)

您可以在Startup.cs的DI配置中实现此目的。

它们的密钥是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>();
                }
            });