帮助解决 ASP.NET Core 3.1 应用程序中的“已有一个与此命令关联的打开的 DataReader 必须首先关闭”

Yan*_*aya 3 c# asp.net entity-framework-core asp.net-core

我需要帮助修复错误消息:

已经有一个打开的 DataReader 与此命令关联,必须先将其关闭。

问题源于以下行:

foreach (var user in _userManager.Users) {
Run Code Online (Sandbox Code Playgroud)

我知道基本上,有另一个阅读器打开,另一个尚未关闭,但我不知道如何根据我的代码解决此问题。我四处寻找类似的问题,有很多,有些建议不添加跟踪,有些建议返回.ToList。我有点不知所措,需要一些帮助。

到目前为止,这是我的代码:

控制器

角色管理器和角色管理器都UserManager被注入,在本例中,我使用自己的 ApplicationUser 扩展了身份类。据我了解,在注入后,声明带有的项目_将不应用任何跟踪,这可能是错误的。

  private readonly RoleManager<IdentityRole> _roleManager;
  private readonly UserManager<ApplicationUser> _userManager;

  public AdministrationController(RoleManager<IdentityRole> roleManager,
         UserManager<ApplicationUser> userManager) 
  {
      _roleManager = roleManager;
      _userManager = userManager;
  }

[HttpGet]
public async Task<IActionResult> EditRole(string id)
{
    var role = await _roleManager.FindByIdAsync(id);

    if (role == null) {
        ViewBag.ErrorMessage = $"Role with Id = {id} cannot be found";
        return View("NotFound");
    }
    var model = new EditRoleViewModel
    {
        Id = role.Id,
        RoleName = role.Name
    };
    foreach (var user in _userManager.Users) {
        if (await _userManager.IsInRoleAsync(user, role.Name))
        {
            model.Users.Add(user.UserName);
        }
     }
     return View(model);
 }
Run Code Online (Sandbox Code Playgroud)

模型

为了避免@if (Model.Users.Any())视图出现错误,Users集合在 中实例化ViewModel

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace WebApplication1
{
    public class EditRoleViewModel
    {
        public EditRoleViewModel() {
            Users = new List<string>();
        }
        public string Id { get; set; }
        [Required(ErrorMessage = "Role name is required")]
        public string RoleName { get; set; }
        public List<string> Users { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

看法

@model EditRoleViewModel
@{ 
    ViewBag.Title = "Edit Role";
}
<h1>Edit Role</h1>

<form method="post" class="mt-3">
    <div class="form-group row">
        <label asp-for="Id" class="col-sm-2 col-form-label"></label>
        <div class="col-sm-10">
            <input asp-for="Id" disabled class="form-control" />
        </div>
    </div>
    <div class="form-group row">
        <label asp-for="RoleName" class="col-sm-2 col-form-label"></label>
        <div class="col-sm-10">
            <input asp-for="RoleName" class="form-control" />
            <span asp-validation-for="RoleName" class="text-danger"></span>
        </div>
    </div>

    <div asp-validation-summary="All" class="text-danger"></div>

    <div class="form-group row">
        <div class="col-sm-10">
            <button type="submit" class="btn btn-parimary">Update</button>
            <a asp-action="ListRoles" class="btn btn-danger">Cancel</a>
        </div>
    </div> 

    <hr />
    <div class="card">
        <div class="card-header">
            <h3>Users in this role</h3>
        </div>
        <div class="card-body">
            @if (Model.Users.Any())
            {
                foreach (var user in Model.Users)
                {
                    <h5 class="card-title">@user</h5>
                }
            }
            else { 
                <h5 class="card-title">None at the moment</h5>
            }
        </div>
        <div class="card-footer">
            <a href="#" class="btn btn-primary" style="width: auto">Add Users</a>
            <a href="#" class="btn btn-primary" style="width: auto">Remove Users</a>
        </div>
    </div>    
</form>
Run Code Online (Sandbox Code Playgroud)

如有任何帮助,我们将不胜感激。

jde*_*rth 10

看来您正在尝试_userManager在循环内使用同一对象,该循环也使用该对象来迭代您的用户。我相信UserManager有一个开放的连接,它使用DataReader, 来查询您的用户列表。

修改foreach循环以迭代完整的用户列表,或者将用户列表分配给局部变量,然后迭代该变量。_userManager.Users.ToList()无论哪种方式,您都需要在迭代该结果集之前调用。然后您应该能够_userManager从循环内访问。

我相信有一种方法可以启用多个活动结果集,但就您而言,我认为没有必要。此外,当使用 DbContext 进行查询时,无跟踪仅适用于 EF Core。无跟踪指示 DbContext 不跟踪其更改跟踪器中的实体,这可能会在更新实体时导致问题。

foreach (var user in _userManager.Users.ToList()) 
{
    if (await _userManager.IsInRoleAsync(user, role.Name))
    {
        model.Users.Add(user.UserName);
    }
}
Run Code Online (Sandbox Code Playgroud)

或者您可以使用异步方法并将其分配给变量。

var userList = await _userManager.Users.ToListAsync();

foreach (var user in userList) 
{
    if (await _userManager.IsInRoleAsync(user, role.Name))
    {
        model.Users.Add(user.UserName);
    }
}
Run Code Online (Sandbox Code Playgroud)

我相信这是指 LINQ 查询中的延迟执行。如果您想更好地理解,可以在文档中阅读它。