.NET Core 3.1更新Session触发IFeatureCollection已释放错误如何解决
问题根因
- 核心原因:ASP.NET Core 中
HttpContext的生命周期和当前请求完全绑定,你在Start方法中同步执行5-10分钟的阻塞任务,远超过默认请求超时时间,请求被回收后HttpContext连带其关联的Session对象都会被释放,后续访问Session就会触发IFeatureCollection已释放的错误。 - 场景错误:
Session本身是会话级别的存储,设计用途不是存储长耗时任务的实时进度,且你用的内存型Session服务重启后数据就会丢失。 - 代码错误:
Start方法被标记为async Task但内部没有任何await逻辑,会引发编译告警,且同步阻塞会占用请求线程导致服务并发能力下降;另外Start方法为private修饰符,无法作为控制器Action被前端直接请求。
解决方案
1. 替换进度存储方案
不要用Session存储进度,新建线程安全的单例进度服务,以用户唯一标识为键存储不同用户的进度数据:
public class ProgressService { // 线程安全字典:key=用户唯一ID,value=(进度值,状态文本,同步是否完成) private readonly ConcurrentDictionary<string, (int Percentage, string Status, bool SyncCompleted)> _progressStore = new(); public void Update(string userId, int percentage, string status, bool isCompleted = false) { _progressStore.AddOrUpdate(userId, (percentage, status, isCompleted), (k, old) => (percentage, status, isCompleted)); } public (int Percentage, string Status, bool SyncCompleted) Get(string userId) { return _progressStore.TryGetValue(userId, out var val) ? val : (0, "未开始", false); } public void Clear(string userId) => _progressStore.TryRemove(userId, out _); }
在Startup.cs的ConfigureServices方法中注册服务:
services.AddSingleton<ProgressService>();
2. 改造Start方法
不要在请求线程中执行耗时任务,收到请求后初始化进度,将任务扔到后台执行,立刻返回响应避免请求超时:
// 注入ProgressService private readonly ProgressService _progressService; public HomeController(ProgressService progressService) { _progressService = progressService; } // 改成public,去掉不必要的async标记 public IActionResult Start(string token, string btnClick) { // 提前获取当前用户ID,不要在后台任务中访问HttpContext var userId = User.FindFirstValue(System.Security.Claims.ClaimTypes.NameIdentifier); // 初始化进度 _progressService.Update(userId, 10, "正在获取用户信息..."); // 后台执行耗时任务,不阻塞当前请求 _ = Task.Run(() => { try { GetUserDetails(); _progressService.Update(userId, 35, "正在获取其他数据..."); GetOtherData(); _progressService.Update(userId, 55, "处理完成", true); // 后续任务完成后更新进度到100即可 } catch (Exception ex) { _progressService.Update(userId, 0, $"处理失败:{ex.Message}", true); } }); // 立刻返回视图 return View(); }
3. 改造GetData方法
从进度服务读取数据,不再使用Session:
public JsonResult GetData() { var userId = User.FindFirstValue(System.Security.Claims.ClaimTypes.NameIdentifier); var progress = _progressService.Get(userId); var result = $"{progress.Percentage},{progress.Status},{progress.SyncCompleted}"; return Json(result); }
注意事项
- 如果你的任务耗时超过10分钟,建议用
IHostedService或者Hangfire这类专业后台任务框架处理,避免ASP.NET Core进程回收时任务被强制终止 - 后台任务中禁止直接引用请求生命周期的对象(比如HttpContext、Scoped服务),如果需要使用Scoped服务,要手动创建作用域获取实例
- 你原有Startup中的Session配置本身无问题,只是使用场景不匹配,不需要修改Session相关配置
内容的提问来源于stack exchange,提问作者Asif Hussain
相关产品推荐
相关产品推荐

