.NET多租户中间件如何正确使用Scoped服务ITenantProvider?
解决.NET中间件中注入Scoped服务的错误:Cannot resolve scoped service from root provider
你遇到的错误本质是中间件是单例生命周期,而构造函数中直接注入Scoped服务ITenantProvider会导致依赖解析冲突——单例服务只能依赖单例或瞬时服务,不能直接依赖Scoped服务,否则会试图从根服务提供者(不关联请求scope)解析Scoped服务,从而报错。
以下是两种可行的解决方案,都能保证中间件和下游服务使用同一个请求scope内的ITenantProvider实例:
方案1:在Invoke方法中通过HttpContext获取Scoped服务
修改TenantMiddleware,构造函数仅保留RequestDelegate,在Invoke方法内通过当前请求的HttpContext.RequestServices获取ITenantProvider的Scoped实例:
public class TenantMiddleware { private readonly RequestDelegate _next; public TenantMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { // 从当前请求的服务scope中获取Scoped的ITenantProvider实例 var tenantProvider = context.RequestServices.GetRequiredService<ITenantProvider>(); // 这里执行设置租户ID的逻辑,示例从请求头提取 if (Guid.TryParse(context.Request.Headers["X-Tenant-Id"], out var tenantId)) { tenantProvider.SetTenantId(tenantId); } await _next(context); } }
方案2:利用.NET Core 3.0+的Invoke方法参数注入
.NET Core 3.0及以上版本支持在Invoke(或InvokeAsync)方法中直接注入Scoped服务,框架会自动从当前请求的服务scope中解析:
public class TenantMiddleware { private readonly RequestDelegate _next; public TenantMiddleware(RequestDelegate next) { _next = next; } // 直接将ITenantProvider作为Invoke方法的参数,框架自动注入当前请求的Scoped实例 public async Task Invoke(HttpContext context, ITenantProvider tenantProvider) { // 设置租户ID的逻辑,示例从请求头提取 if (Guid.TryParse(context.Request.Headers["X-Tenant-Id"], out var tenantId)) { tenantProvider.SetTenantId(tenantId); } await _next(context); } }
验证下游服务的正确性
你的启动配置已经符合要求:
ITenantProvider注册为Scoped,确保每个请求有独立实例DBService注册为Transient,每次获取都会创建新实例,但会注入当前请求的ITenantProvider实例
这样当中间件设置好租户ID后,下游的DBService在构造时拿到的ITenantProvider和中间件使用的是同一个请求scope内的实例,调用GetTenantId()就能获取到正确的租户ID。
内容的提问来源于stack exchange,提问作者Shahar Shokrani
相关产品推荐
相关产品推荐

