需要帮助检查并将用户异步添加到多个角色

Posted

技术标签:

【中文标题】需要帮助检查并将用户异步添加到多个角色【英文标题】:Need help checking and adding user to multiple roles async 【发布时间】:2014-10-09 07:08:28 【问题描述】:

我正在使用 ASP.Net MVC 5 并正在创建一个控制器以允许经理将用户添加到角色。这是控制器的非异步版本的样子:

public JsonResult UerRoles(string userid, string[] roles)

   foreach(var role in roles)
   
      if(!UserManager.IsInRole(userid, role))
      
         UserManager.AddToRole(userid, role);
      
    

    ...
 

我想使用 UserManager.IsInRoleAsync 和 UserManager.AddToRoleAsync 方法,并希望并行运行这些方法,然后暂停线程的执行,直到一切都完成。我以前没有做过异步线程,但似乎我可以做这样的事情:

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);

   ...
 

但是,我不知何故需要考虑检查用户是否已经在角色中的条件逻辑——即 UserManager.IsInRoleAsycn(userid, role)。我不确定如何检查并有条件地将用户添加到角色中,所有这些都是并行和异步的。

我已经看到提到的 ContinueWith 方法,它似乎可能以某种方式适用,但我无法弄清楚它是如何实现的。比如:

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

有没有办法做到这一点?

【问题讨论】:

我认为你根本不需要使用Tasks。等待Async 方法 WhenAll 不会并行运行任务。为此,您实际上必须为每个任务生成一个新线程,这首先违背了异步的目的。 【参考方案1】:

假设:您正在使用 AspNet Identity 的标准 EF 实现

您必须使用此代码:

foreach(var role in roles)

   var isInRole = await UserManager.IsInRoleAsync(userid, role)
   if(!isInRole)
   
      await UserManager.AddToRoleAsync(userid, role);
   

这个底层 ORM(实体框架)的原因是不支持在并行线程中执行 sql 查询。如果你尝试这样做,你会得到 EF 的异常,说这样的话:

在前一个异步操作之前在此上下文上启动了第二个操作 操作完成。使用“等待”确保任何异步操作 在此上下文中调用另一个方法之前已完成。任何实例 成员不能保证是线程安全的。

This topic 有一个很好的例子来说明发生了什么。

另外,我怀疑将用户添加到角色是您系统中非常流行的操作。为什么要不遗余力地优化那些不会经常被击中的东西?

更新

您无需检查IsUserInRole。身份框架无论如何都会为你做这件事。这是反编译的身份框架的一部分:

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;

【讨论】:

非常感谢。从未考虑过 EF 的影响。 请注意,如果您选择不测试 IsInRole,那么您将必须添加额外的成功测试。如果反编译的 AddToRoleAsync(以及 AddToRole)代码,则表明 Identity 结果不成功。 IdentityResult.Succeeded 将为 FALSE。并且您必须对其进行测试,或测试“已添加到角色”的错误并忽略它。 IMO - 坚持使用UserManager.IsInRoleAsync(userid, role) 会更加稳健

以上是关于需要帮助检查并将用户异步添加到多个角色的主要内容,如果未能解决你的问题,请参考以下文章

在 Laravel 中创建具有角色的用户时需要帮助

命令删除所有角色并将特定角色添加到提到的用户

如何在 Spring WebFlux Security(Reactive Spring Security)配置中将多个用户角色添加到单个 pathMatcher/Route?

Spring Boot中基于​​角色的授权

添加多个用户会创建多个角色

通过python(pymongo)向mongodb中的用户添加角色