ASP.NET Core中EF Core遇“Invalid attempt to call Read when reader is closed”错误求助
问题分析与解决
错误根源
你代码里的核心问题是在foreach循环内部调用了SchContext.Dispose()。第一次循环执行时就释放了EF Core上下文,直接关闭了数据库连接,后续循环迭代再尝试访问SchContext.QFile或查询lessons时,数据库读取器已被关闭,因此抛出Invalid attempt to call Read when reader is closed错误。
另外,你之前添加的MultipleActiveResultSets=true是用来解决多结果集并行读取的问题,和当前错误无关。
修复步骤
1. 移除循环内的Dispose调用
把SchContext.Dispose()移到整个循环结束之后,确保上下文在所有数据库操作完成后再释放:
public IActionResult Index() { List<ShowQuestionViewModel> showQuestions = new List<ShowQuestionViewModel>(); foreach(var file in SchContext.QFile) { var lesson = SchContext.lessons.Include(lesson => lesson.field).Include(lesson => lesson.level) .SingleOrDefault(l => l.Id == file.Id); var SampleQ = new ShowQuestionViewModel() { qfile = file, lessonName = lesson?.Name, // 增加空值保护,避免lesson为null时报错 FieldName = lesson?.field?.Name, LevelName = lesson?.level?.Name, }; showQuestions.Add(SampleQ); } SchContext.Dispose(); // 移至循环外部 return View(showQuestions); // 补充返回逻辑,原代码遗漏 }
2. 更规范的上下文管理:使用using块
EF Core上下文推荐用using块自动管理生命周期,避免手动Dispose时出错:
public IActionResult Index() { List<ShowQuestionViewModel> showQuestions = new List<ShowQuestionViewModel>(); using(var schContext = new SchContext()) // 假设SchContext是你的上下文类 { foreach(var file in schContext.QFile) { var lesson = schContext.lessons.Include(lesson => lesson.field) .Include(lesson => lesson.level) .SingleOrDefault(l => l.Id == file.Id); var SampleQ = new ShowQuestionViewModel() { qfile = file, lessonName = lesson?.Name, FieldName = lesson?.field?.Name, LevelName = lesson?.level?.Name, }; showQuestions.Add(SampleQ); } } // using块结束时自动释放上下文 return View(showQuestions); }
3. 优化查询:避免N+1性能问题
当前代码会触发N+1查询(1次查询QFile,N次查询lessons),性能低下。可以用EF Core的Join关联查询,一次性获取所有数据:
public IActionResult Index() { using(var schContext = new SchContext()) { var showQuestions = schContext.QFile .Join(schContext.lessons.Include(l => l.field).Include(l => l.level), file => file.Id, lesson => lesson.Id, (file, lesson) => new ShowQuestionViewModel { qfile = file, lessonName = lesson.Name, FieldName = lesson.field.Name, LevelName = lesson.level.Name }) .ToList(); return View(showQuestions); } }
这样仅需一次数据库查询就能获取所有需要的数据,同时彻底避免循环内的上下文问题。
额外注意事项
- 如果你的
SchContext是通过依赖注入获取的(比如在Program.cs中注册为Scoped),不要手动调用Dispose(),框架会在请求结束时自动释放上下文。 - 始终对
SingleOrDefault返回的结果做空值判断,防止出现NullReferenceException。
内容的提问来源于stack exchange,提问作者محمد مهدی الماسی
相关产品推荐
相关产品推荐

