1

I have 2 solutions that use ASP.NET Core Identity. One is v.2.2 the other is 3.0. They make calls to UserManager.IsInRole(user, role.Name). The code works in v.2.2 but throws an exception, "InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first." in v.3.0. They both are building two list of users, one of users in the target role and the other of user NOT in the target role. The idea is to add or remove users from the target role.

I've searched here, the Microsoft documentation and some blogs, but nothing seems to address my issue

Here is the code in quesion:

    public async Task<IActionResult> OnGetAsync(string id)
    {
        IdentityRole role = await roleManager.FindByIdAsync(id);
        List<FREEIncUser> members = new List<FREEIncUser>();
        List<FREEIncUser> nonMembers = new List<FREEIncUser>();
        foreach (FREEIncUser user in userManager.Users)
        {
            var list = await userManager.IsInRoleAsync(user, role.Name) ? members : nonMembers;
            list.Add(user);
        } // end foreach (FREEIncUser user in userManager.Users)

        RoleUserViewModel = new RoleUserViewModel
        {
            Role = role,
            Members = members,
            NonMembers = nonMembers
        };

        return Page();
    } // end public async Task<IActionResult> OnGetAsync(string id)

Here are some screenshots: v.2.2 Invoke ManageUsers for a role(v.2.2)

Which gives: List of users in and not in role

v.3.0 Invoke ManageUsers for a role(v.3.0)]

This generates an exception: An unhandled exception occurred while processing the request. InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.

Microsoft.Data.SqlClient.SqlCommand+<>c.b__164_0(Task result)

Stack Query Cookies Headers Routing 

InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
    Microsoft.Data.SqlClient.SqlCommand+<>c.<ExecuteDbDataReaderAsync>b__164_0(Task<SqlDataReader> result)
    System.Threading.Tasks.ContinuationResultTaskFromResultTask<TAntecedentResult, TResult>.InnerInvoke()
    System.Threading.Tasks.Task+<>c.<.cctor>b__274_0(object obj)
    System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, object state)
    System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref Task currentTaskSlot, Thread threadPoolThread)
    Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor+AsyncQueryingEnumerable<T>+AsyncEnumerator.MoveNextAsync()
    System.Runtime.CompilerServices.ValueTaskAwaiter<TResult>.GetResult()
    Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync<TSource>(IAsyncEnumerable<TSource> asyncEnumerable, CancellationToken cancellationToken)
    Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync<TSource>(IAsyncEnumerable<TSource> asyncEnumerable, CancellationToken cancellationToken)
    Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore<TUser, TRole, TContext, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken)
    Microsoft.AspNetCore.Identity.UserManager<TUser>.IsInRoleAsync(TUser user, string role)
    FREEInc.WebUI.Pages.RoleAdmin.ManageUsersModel.OnGetAsync(string id) in ManageUsers.cshtml.cs

                    var list = await userManager.IsInRoleAsync(user, role.Name) ? members : nonMembers;
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory+GenericTaskHandlerMethod.Convert<T>(object taskAsObject)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory+GenericTaskHandlerMethod.Execute(object receiver, object[] arguments)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

The throwing line is:

var list = await userManager.IsInRoleAsync(user, role.Name) ? members : nonMembers;

Any help and guidance would be greatly appreciated. Thank you.

Simply Ged
  • 8,250
  • 11
  • 32
  • 40
user3802434
  • 602
  • 1
  • 7
  • 14
  • When the exception is thrown, what is the **exact** value of `members.Count`? `nonMembers.Count`? – mjwills Oct 28 '19 at 02:03
  • In the .NET Core 3 solution, the members count is 1 and the non-members count is 0. In the 2.2 version the members count is 1; the non-members count is 4. – user3802434 Oct 29 '19 at 15:35

2 Answers2

4

try this code

foreach (FREEIncUser user in userManager.Users.ToList())
{
    var list = await userManager.IsInRoleAsync(user, role.Name) ? members : nonMembers;
    list.Add(user);
}

The problem is you can't have multiple connection at the same time on a DbContext and when you have a foreach on userManager.Users the connection is open and the records are returning one by one and you can have another query (IsInRole) on the same DbContext.

By adding a ToList() to userManager.Users you are getting all the users and the iterating on them.

Kahbazi
  • 14,331
  • 3
  • 45
  • 76
  • 1
    This is the answer. I had foreach (var role in _roleManager.Roles) { (await _userManager.IsInRoleAsync(user, role.Name)) " and it was erroring out on This MySqlConnection is already in use. See https://fl.vu/mysql-conn-reused Changed it to foreach (var role in _roleManager.Roles.ToList()){ if (await _userManager.IsInRoleAsync(user, role.Name))} and it fixed it right up – BrownPony May 16 '21 at 05:08
  • Imho, this should _not_ be the solution. `userManager.Users.ToList()` introduces a blocking database access, but `OnGetAsync()` is declared `async` which must be valid for the whole method. Allowing multiple active result sets (see Tomi's answer) is probably the better approach. – Ingmar Dec 11 '22 at 12:16
2

Add this to your connection string: MultipleActiveResultSets=True;

Tomi
  • 21
  • 2