Need help validating and adding user to multiple async roles

I am using ASP.Net MVC 5 and am creating a controller to allow a manager to add a user to roles. This would look like a non-asynchronous version of the controller:

public JsonResult UerRoles(string userid, string[] roles)
{
   foreach(var role in roles)
   {
      if(!UserManager.IsInRole(userid, role))
      {
         UserManager.AddToRole(userid, role);
      }
    }

    ...
 }

      

I want to use the UserManager.IsInRoleAsync and UserManager.AddToRoleAsync methods and would like to run them in parallel and then stop the thread from executing until everything is complete. I haven't done asynchronous streaming before, but it looks like I could do something like this:

public async Task<JsonResult> UserRoles(string userid, string[] roles)
{
   IList<Task<IdentityResult>> tasks = new List<Task<bool>>();
   foreach(var role in roles)
   {
      tasks.Add(UserManager.AddToRoleAsync(userid, role));
   }

   await Task.WhenAll(tasks);

   ...
 }

      

However, for some reason I need to take into account the conditional logic of checking if the user is already in the role - that is, UserManager.IsInRoleAsycn (userid, role). I'm not sure how to test this and conditionally add users to roles all in parallel and asynchronously.

I have seen the ContinueWith method mentioned and it seems that it might be applied in one way or another, but I cannot figure out how this would be implemented. Something like:

UserManager.IsInRoleAsync(userid, role).ContinueWith(t => if(t.Result) { UserManager.AddToRole(userid, role)} ;);

      

Is there a way to do this?

+3


source to share


1 answer


Assumption: . You are using the standard EF implementation for AspNet Identity

You will need to work with this code:

foreach(var role in roles)
{
   var isInRole = await UserManager.IsInRoleAsync(userid, role)
   if(!isInRole)
   {
      await UserManager.AddToRoleAsync(userid, role);
   }
}

      

The reason behind this is the underlying ORM (Entity Framework) does not support executing sql queries in parallel threads. If you try this, you will get an exception from EF by saying something like this:

The second operation started in this context before the previous asynchronous completion of the operation. Use "wait" to ensure that any asynchronous operations have completed before calling another method in this context. Any member instance is not guaranteed to be thread safe.



This section has a good example of what's going on.

Also, I doubt adding a user to a role is a very popular operation on your system. Why go out of your way to optimize something that doesn't hit frequently?

Update

You don't need to check for IsUserInRole

. The identity framework does it anyway for you. This is part of the decompiled Identity system:

public virtual async Task<IdentityResult> AddToRoleAsync(TKey userId, string role)
{
    // sanity checks... 

    IList<string> userRoles = await userRoleStore.GetRolesAsync(user).ConfigureAwait(false);
    IdentityResult identityResult;
    if (userRoles.Contains(role))
    {
      identityResult = new IdentityResult(new string[1]
      {
        Resources.UserAlreadyInRole
      });
    }
    else
    {
    // actually add user to role
    }
    return identityResult;
}

      

+2


source







All Articles