调用RoleManager.FindByIdAsync提示存在未关闭关联DataReader如何解决?
问题根源
该异常由同一个数据库连接上同时存在多个激活的DataReader查询导致:userManager.Users为EF Core返回的延迟加载IQueryable对象,直接遍历该对象时,数据库会保持DataReader打开状态逐行返回数据,此时循环内部发起的await userManager.IsInRoleAsync(...)会尝试复用同一连接发起新查询,进而触发冲突。若视图中为循环渲染多个角色的<td>标签,前一个TagHelper的数据库查询未释放连接,也会导致后续的FindByIdAsync调用触发该异常。
解决方案
方案1:调用Identity内置方法查询角色用户(最优)
Identity的UserManager已经提供了直接查询指定角色下所有用户的内置方法GetUsersInRoleAsync,不需要手动遍历全量用户,既避免了DataReader冲突,也大幅提升了查询性能:
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { List<string> names = new List<string>(); IdentityRole role = await roleManager.FindByIdAsync(Role); if (role != null) { // 直接调用内置方法查询该角色下的所有用户,无需遍历全量用户 var usersInRole = await userManager.GetUsersInRoleAsync(role.Name); names.AddRange(usersInRole.Select(u => u.UserName)); } output.Content.SetContent(names.Count == 0 ? "No Users" : string.Join(", ", names)); }
方案2:立即加载全量用户到内存
如果需要保留原有遍历逻辑,只需要在访问Users属性时追加.ToList(),一次性把所有用户加载到内存、关闭DataReader后再遍历即可:
// 加ToList()立即执行查询,关闭DataReader后再遍历 foreach (var user in userManager.Users.ToList()) { if (user != null && await userManager.IsInRoleAsync(user, role.Name)) names.Add(user.UserName); }
方案3:开启MARS特性(临时兼容方案)
如果使用的是SQL Server数据库,可以在数据库连接字符串中添加MultipleActiveResultSets=True参数,允许同一个连接上同时存在多个激活的查询结果集。该方案仅作为临时兼容使用,不推荐作为长期解决方案,无法解决全量遍历用户带来的性能损耗问题。
内容的提问来源于stack exchange,提问作者YL2120
相关产品推荐
相关产品推荐

