简体   繁体   English

在 ASP.NET Core 中间件中注入服务

[英]Injecting Service in Middleware in ASP.NET Core

I want to inject a service based on the HTTP header value.我想根据 HTTP 标头值注入一个服务。 So I have 2 classes - DbDataProvider and InMemDataProvider, both are implemented from IDataProvider.所以我有 2 个类 - DbDataProvider 和 InMemDataProvider,它们都是从 IDataProvider 实现的。 Whenever an API call is made, a header is passed by the client which determines whether DbDataProvider is required or InMemDataProvider is required.每当进行 API 调用时,客户端都会传递一个标头,以确定是需要 DbDataProvider 还是需要 InMemDataProvider。 How do I achieve that?我如何做到这一点? So in short I need to inject service in the ServiceCollection in one of the middlewares.所以简而言之,我需要在其中一个中间件的 ServiceCollection 中注入服务。 Is that possible?这可能吗?

The problem is that in the ConfigureService method in Startup class I cannot get the HttpContext.问题是在 Startup 类的 ConfigureService 方法中,我无法获取 HttpContext。 I have written a middleware using which I am able to get the HTTP Context but how do I inject a Service there?我已经编写了一个中间件,我可以使用它来获取 HTTP 上下文,但是如何在那里注入服务?

There is no easy or clean way to do this.没有简单或干净的方法可以做到这一点。 You can't modify the IServiceCollection outside of the ConfigureServices method.您不能在ConfigureServices方法之外修改IServiceCollection But even if you could, it's of no use, because the container has already been built before Configure is being called.但即使可以,也无济于事,因为在调用Configure之前容器已经构建。

What you could do is create a factory class and register it as scoped.您可以做的是创建一个工厂类并将其注册为作用域。

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>();
    }
}

Then in your middleware:然后在你的中间件中:

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

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

and in your controller/services:并在您的控制器/服务中:

public class MyController : Controller 
{
    private readonly IDataProvider dataProvider;

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

You can achieve this in your DI config in Startup.cs .您可以在Startup.cs中的 DI 配置中实现这一点。

They key is services.AddHttpContextAccessor() which allows you to get access to the HttpContext.它们的关键是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>();
    }
});

The answer above from Tsen is correct. Tsen上面的答案是正确的。 You should implement a factory.你应该实现一个工厂。

But in addition you can also register factory methods to the services collection.但除此之外,您还可以向服务集合注册工厂方法。 Like so:像这样:

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

This registers your IDataProvider.这将注册您的 IDataProvider。 In the Create you should evaluate that HTTP header value so it returns the correct IDataProvider instance.在 Create 中,您应该评估该 HTTP 标头值,以便它返回正确的 IDataProvider 实例。 Then in any class you need it you can simply request IDataProvider via the constructor and the correct implementation will be provided by the container.然后在您需要它的任何类中,您可以简单地通过构造函数请求 IDataProvider,容器将提供正确的实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM