中间件向 DI 容器或服务添加新的依赖项

Middleware add new dependency to DI Container or Services

我正在写一个中间件(也许我想要一个作用域服务??),我想我的计划是有某种多租户场景。

例如,如果我有 2 个域响应此服务:

我想在请求开始时捕获它,查看正在使用的主机名,然后设置一些其他对象以通过依赖注入对管道中的所有内容可用。

似乎中间件应该是实现此目的的正确方法,但不确定如何执行最后一步。

我的选择似乎是:

中间件

服务

我的假设是该服务能够注册自定义作用域对象,因为它仍在 startup.cs

ConfigureServices 方法中

但是,对于中间件,它是通过 Configure 方法初始化的,此时 DI 容器已经构建?

您可以将 AddScoped 的工厂重载用于您希望根据 tenant/request 不同的服务。这是一个例子:

services.AddScoped<IServiceForTenant>(sp =>
{
    var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
    var serviceForTenant = new ServiceForTenant();

    // TODO: Use httpContextAcccessor.HttpContext to configure serviceForTenant.

    return serviceForTenant;
});

对于进入您的 ASP.NET 核心应用程序的每个请求,当您首次请求 IServiceForTenant 时,上面的代码将 运行 例如一个控制器。此时,您的代码可以从 IHttpContextAccessor.HttpContext 读取并做出所需的任何决定,以便为 IServiceForTenant 创建实现实例。这个相同的实例将用于请求的其余部分(即进一步向上的管道)。

传递给 AddScoped 的参数是 Func<IServiceProvider, T>。您需要在此处提供的只是某种委托,这可以通过多种方式之一来完成。下面是一些示例:

  1. 您可以将调用包装到它自己的扩展方法中,如下所示:

    public static void AddServiceForTenant(this IServiceCollection services)
    {
        services.AddScoped<IServiceForTenant>(sp =>
        {
            // ...
        });
    }
    

    ConfigureServices中:

    services.AddServiceForTenant();
    
  2. 使用 class 和 static 方法:

    public static class ServiceForTenantFactory
    {
        public static ITenantForService Create(IServiceProvider sp)
        {
            // ...
        }
    }
    

    ConfigureServices中:

    services.AddScoped(ServiceForTenantFactory.Create);
    
  3. 使用实例方法 class:

    public class ServiceForTenantFactory
    {
        public ITenantForService Create(HttpContext httpContext)
        {
            // ...
        }
    }
    

    ConfigureServices中:

    services.AddScoped(sp =>
    {
        var httpContextAccessor = sp.GetRequiredService<IHttpContextAccessor>();
        var serviceForTenantFactory = new ServiceForTenantFactory(); // Or use DI.
    
        return serviceForTenantFactory.Create(httpContextAccessor.HttpContext);
    });
    

    最后一个选项是最灵活的,因为您甚至可以从 DI 中解析 ServiceForTenantFactory 本身并且它可以有自己的依赖项等。另请注意 Create 这里采用 HttpContext直接(作为例子)。

正如我已经说过的,还有比显示的三个更多的选项,但这应该是一个很好的基础。