C # Entity Framework error on async methods

I've seen this before, but I have a different problem.

I have this service class for managing ASP.NET identity roles:

public class RoleService : IRoleService
{
    private readonly RoleManager<ApplicationRole> _roleManager;

    public RoleService(RoleManager<ApplicationRole> roleManager)
    {
        this._roleManager = roleManager;
    }

    public async Task<IdentityResult> CreateAsync(ApplicationRole role)
    {
        return await this._roleManager.CreateAsync(role);
    }
}

      

As suggested by this question , I use a method CreateAsync

like this to avoid using LINQ foreach

:

private async Task PopulateRoles()
{
     var roles = new[] { "A", "B", "C", "D" };

     // Used LINQ foreach previously but I coded this way instead to follow the related questions answer
     var tasks = roles.Select(role =>
                           this._roleService.CreateAsync(new ApplicationRole(role)))
                      .ToList();

     await Task.WhenAll(tasks);
}

      

However, this results in a runtime error await this.PopulateRoles()

.

Entity Framework: There is already an open DataReader associated with this command, which should be closed first.

Searching for this error only leads to a suggestion to add ToList()

LINQ to my selection. How can I fix this?

+3


source to share


1 answer


The problem lies with RoleManager<T>

, which internally gets one DbContext

, as we can see here :

public class RoleStore<TRole, TContext, TKey> : 
        IQueryableRoleStore<TRole>, 
        IRoleClaimStore<TRole>
        where TRole : IdentityRole<TKey>
        where TKey : IEquatable<TKey>
        where TContext : DbContext
{
    public RoleStore(TContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }
        Context = context;
    }
}

      

DbContext

itself cannot handle concurrent calls. An alternative would be to do each of the calls inside foreach

and await

each of them:



private async Task PopulateRoles()
{
     var roles = new[] { "A", "B", "C", "D" };

     foreach (var role in roles)
     {
         await _roleService.CreateAsync(new ApplicationRole(role));
     }
}

      

Thus, while you cannot use all roles at the same time, you still use the asynchronous nature of the IO call instead of the blocking synchronous call.

+4


source







All Articles