I'm using ASP.Net MVC 5 and am creating a controller to allow a manager to add a user to roles. This is what a non-async version of the controller would look like:
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 these in parallel and then halt execution of the thread until everything is completed. I've not done asynchronous threading before, but it appears that 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, I somehow need to account for the conditional logic of checking if the user is already in the role -- i.e. UserManager.IsInRoleAsycn(userid, role). I'm not sure how to check for that and conditionally add Users to roles all in parallel and asynchronously.
I've seen the ContinueWith method mentioned and it seems like maybe that somehow applies but I can't figure out how that would be implemented. Something like:
UserManager.IsInRoleAsync(userid, role).ContinueWith(t => if(t.Result) { UserManager.AddToRole(userid, role)} ;);
Is there a way to accomplish this?
Assumption: You are using standard EF implementation of AspNet Identity
You will have 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 for this underlying ORM (Entity Framework) does not support execution of sql queries in parallel threads. If you try that, you will get exception from EF saying something like this:
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
This topic has a good example of what happens.
Also, I doubt that adding user to a role is a very popular operation in your system. Why go out of your way to optimise something that will not be hit that often?
Update
You don't need to do checking for IsUserInRole. Identity framework does it anyway for you. This is a part of decompiled Identity framework:
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With