作用域服务对象在中间件赋值后并行请求时有时为空的问题
问题原因及解决方案
核心原因
你遇到的问题大概率是**UserSettings类内部使用了静态成员存储用户信息**。虽然你用AddScoped注册了服务,每个请求会生成独立的UserSettings实例,但如果类里的属性是静态的,所有实例都会共享这部分状态——并行请求时,多个请求的设置操作会互相覆盖,最终只有一个请求的信息能保留下来。
另外也有可能是注册服务时误使用了AddSingleton(但你明确说明用了AddScoped,这条可能性极低),或者中间件里的设置逻辑误操作了全局共享资源。
解决办法
1. 修正UserSettings类的实现
把类里的静态成员改成实例成员,让每个请求的UserSettings实例拥有独立的状态:
public interface IUserSettings { string UserId { get; set; } // 其他用户信息属性 } public class UserSettings : IUserSettings { // 用实例属性,每个Scoped实例独立存储 public string UserId { get; set; } // 其他实例属性... }
2. 确保中间件逻辑正确
中间件的InvokeAsync方法里,直接操作注入的当前请求专属的userSettings实例即可,不需要额外处理线程问题——因为AddScoped已经保证每个请求有独立的实例:
public async Task InvokeAsync(HttpContext context, IUserSettings userSettings, IHttpClientFactory httpClientFactory) { // 示例:从请求中获取用户ID并设置到当前实例 var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; userSettings.UserId = userId; // 继续处理请求 await _next(context); }
3. 关于HttpContext.Items的正确用法
如果你已经改用HttpContext.Items存储,要确保存入和取出的是当前请求的上下文:
- 中间件存入:
context.Items["UserSettings"] = userSettings; - 后续在控制器/服务中取出(需要注入
IHttpContextAccessor):public class SomeService { private readonly IHttpContextAccessor _httpContextAccessor; public SomeService(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public void DoSomething() { if (_httpContextAccessor.HttpContext?.Items.TryGetValue("UserSettings", out var settings) == true && settings is IUserSettings userSettings) { // 使用userSettings } } }
注意:使用IHttpContextAccessor需要先注册服务:
services.AddHttpContextAccessor();
不过更推荐用AddScoped的方式,因为它更符合依赖注入的设计,代码可读性和可测试性更好,只要类本身是实例成员的实现,就不会有并行请求的冲突问题。
内容的提问来源于stack exchange,提问作者mtl
相关产品推荐
相关产品推荐

