中间件向 DI 容器或服务添加新的依赖项
Middleware add new dependency to DI Container or Services
我正在写一个中间件(也许我想要一个作用域服务??),我想我的计划是有某种多租户场景。
例如,如果我有 2 个域响应此服务:
- www.domain1.com
- www.domain2.com
我想在请求开始时捕获它,查看正在使用的主机名,然后设置一些其他对象以通过依赖注入对管道中的所有内容可用。
似乎中间件应该是实现此目的的正确方法,但不确定如何执行最后一步。
我的选择似乎是:
中间件
- 注册单例服务以访问数据库
- 早点注册成为第一个捕获请求的中间件。
- 分析请求对象并构建自定义配置对象
- 将自定义配置作为作用域对象添加到 DI 容器以供其他服务使用
服务
- 注册单例服务以访问数据库
- 为 IHttpContextAccessor 注册单例服务
- 注册范围?服务——做相当于中间件
- 分析请求对象并构建自定义配置对象
- 将自定义对象注册为 DI 容器中的新作用域对象
我的假设是该服务能够注册自定义作用域对象,因为它仍在 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>
。您需要在此处提供的只是某种委托,这可以通过多种方式之一来完成。下面是一些示例:
您可以将调用包装到它自己的扩展方法中,如下所示:
public static void AddServiceForTenant(this IServiceCollection services)
{
services.AddScoped<IServiceForTenant>(sp =>
{
// ...
});
}
在ConfigureServices
中:
services.AddServiceForTenant();
使用 class 和 static
方法:
public static class ServiceForTenantFactory
{
public static ITenantForService Create(IServiceProvider sp)
{
// ...
}
}
在ConfigureServices
中:
services.AddScoped(ServiceForTenantFactory.Create);
使用实例方法 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
直接(作为例子)。
正如我已经说过的,还有比显示的三个更多的选项,但这应该是一个很好的基础。
我正在写一个中间件(也许我想要一个作用域服务??),我想我的计划是有某种多租户场景。
例如,如果我有 2 个域响应此服务:
- www.domain1.com
- www.domain2.com
我想在请求开始时捕获它,查看正在使用的主机名,然后设置一些其他对象以通过依赖注入对管道中的所有内容可用。
似乎中间件应该是实现此目的的正确方法,但不确定如何执行最后一步。
我的选择似乎是:
中间件
- 注册单例服务以访问数据库
- 早点注册成为第一个捕获请求的中间件。
- 分析请求对象并构建自定义配置对象
- 将自定义配置作为作用域对象添加到 DI 容器以供其他服务使用
服务
- 注册单例服务以访问数据库
- 为 IHttpContextAccessor 注册单例服务
- 注册范围?服务——做相当于中间件
- 分析请求对象并构建自定义配置对象
- 将自定义对象注册为 DI 容器中的新作用域对象
我的假设是该服务能够注册自定义作用域对象,因为它仍在 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>
。您需要在此处提供的只是某种委托,这可以通过多种方式之一来完成。下面是一些示例:
您可以将调用包装到它自己的扩展方法中,如下所示:
public static void AddServiceForTenant(this IServiceCollection services) { services.AddScoped<IServiceForTenant>(sp => { // ... }); }
在
ConfigureServices
中:services.AddServiceForTenant();
使用 class 和
static
方法:public static class ServiceForTenantFactory { public static ITenantForService Create(IServiceProvider sp) { // ... } }
在
ConfigureServices
中:services.AddScoped(ServiceForTenantFactory.Create);
使用实例方法 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
直接(作为例子)。
正如我已经说过的,还有比显示的三个更多的选项,但这应该是一个很好的基础。