asp.net-mvc ASP.NET MVC 5 如何在 Identity 2.0 中删除用户及其相关数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23977036/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 03:42:42  来源:igfitidea点击:

ASP.NET MVC 5 how to delete a user and its related data in Identity 2.0

asp.net-mvcasp.net-mvc-5asp.net-identity

提问by Franva

Hi I'm following this article to delete a user in Identity 2.0 http://www.asp.net/mvc/tutorials/mvc-5/introduction/examining-the-details-and-delete-methods

嗨,我正在按照这篇文章删除 Identity 2.0 中的用户 http://www.asp.net/mvc/tutorials/mvc-5/introduction/examing-the-details-and-delete-methods

However, I need to delete all related records in AspNetUserRoles first and then delete the user.

但是,我需要先删除AspNetUserRoles中的所有相关记录,然后再删除用户。

I found an example which is written in Identity 1.0 and some of methods used inside this example don't exist.

我发现了一个用 Identity 1.0 编写的示例,并且该示例中使用的一些方法不存在。

   // POST: /Users/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> DeleteConfirmed(string id)
        {
            if (ModelState.IsValid)
            {
                if (id == null)
                {
                    return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
                }

                var user = await context.Users.FindAsync(id);
                var logins = user.Logins;
                foreach (var login in logins)
                {
                    context.UserLogins.Remove(login);
                }
                var rolesForUser = await IdentityManager.Roles.GetRolesForUserAsync(id, CancellationToken.None);
                if (rolesForUser.Count() > 0)
                {

                    foreach (var item in rolesForUser)
                    {
                        var result = await IdentityManager.Roles.RemoveUserFromRoleAsync(user.Id, item.Id, CancellationToken.None);
                    }
                }
                context.Users.Remove(user);
                await context.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }
        }

I cannot find IdentityManagerfrom anywhere, and context.Usersdoesn't have FindAsync()method either.

IdentityManager从任何地方都找不到,context.Users也没有FindAsync()方法。

Please help to figure out how to properly delete a User and its related records in Identity 2.0

请帮助弄清楚如何在 Identity 2.0 中正确删除用户及其相关记录

Thanks.

谢谢。

回答by Horizon_Net

I think the classes you're looking for are the UserManagerand the RoleManager. In my opinion they are the better way instead of going against the context directly.

我认为您正在寻找的类是UserManagerRoleManager。在我看来,它们是更好的方法,而不是直接违背上下文。

The UserManagerdefines a method RemoveFromRoleAsyncwhich gives you the ability to remove the user (identified by his key) from a given role. It also defines several Find methods, such as FindAsync, FindByIdAsync, FindByNameAsync, or FindByEmailAsync. They all can be used to retrieve a user. To delete a user you should use the DeleteAsyncmethod which accepts a user object as a parameter. To get the roles a user is member of Identity gives you the GetRolesAsyncmethod where you pass in the ID of the user. Also I see that you're trying to remove a login from a user. For this purpose you should use the RemoveLoginAsyncmethod.

的UserManager定义了一个方法RemoveFromRoleAsync,让你删除的用户从给定角色的能力(通过他的主要标识)。它还定义了多个 Find 方法,例如FindAsyncFindByIdAsyncFindByNameAsyncFindByEmailAsync。它们都可用于检索用户。要删除用户,您应该使用接受用户对象作为参数的DeleteAsync方法。要获取用户是 Identity 成员的角色,您可以使用GetRolesAsync方法,您可以在该方法中传入用户的 ID。我还看到您正在尝试从用户中删除登录名。为此,您应该使用RemoveLoginAsync方法。

All in all your code would look similar to the following one:

总而言之,您的代码将类似于以下代码:

// POST: /Users/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(string id)
{
  if (ModelState.IsValid)
  {
    if (id == null)
    {
      return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

    var user = await _userManager.FindByIdAsync(id);
    var logins = user.Logins;
    var rolesForUser = await _userManager.GetRolesAsync(id);

    using (var transaction = context.Database.BeginTransaction())
    {
      foreach (var login in logins.ToList())
      {
        await _userManager.RemoveLoginAsync(login.UserId, new UserLoginInfo(login.LoginProvider, login.ProviderKey));
      }

      if (rolesForUser.Count() > 0)
      {
        foreach (var item in rolesForUser.ToList())
        {
          // item should be the name of the role
          var result = await _userManager.RemoveFromRoleAsync(user.Id, item);
        }
      }

      await _userManager.DeleteAsync(user);
      transaction.Commit();
    }

    return RedirectToAction("Index");
  }
  else
  {
    return View();
  }
}

You'll need to adjust this snippet to your needs, because I don't have an idea how your IdentityUserimplementation looks like. Remember to declare the UserManageras needed. An example how you could do this can be found when you create a new project in Visual Studio using Individual Accounts.

您需要根据需要调整此代码段,因为我不知道您的IdentityUser实现是什么样的。请记住根据需要声明UserManager。当您使用Individual Accounts在 Visual Studio 中创建新项目时,可以找到如何执行此操作的示例。

回答by wpqs

  • Brad's point about requiring @Html.AntiForgeryToken() in views is not necessary if you are using latest versions of ASP.NET - see AntiForgeryToken still required
  • Why not create a SQL trigger for AspNetUsers so deleting a user also deletes the corresponding records for user from AspNetUserRoles and AspNetUserLogins?
  • I need to invoke DeleteUser from a number of places so I added a static method to AccountController (see below). I'm still learning about MVC, so should be grateful for comments, in particular 1) use of IdentityResult as a return code 2) wisdom of extending AccountController in this way 3) approach for putting password (cleartext) into the Model to validate the action (see sample invocation).

     public static async Task<IdentityResult> DeleteUserAccount(UserManager<ApplicationUser> userManager, 
                                                                             string userEmail, ApplicationDbContext context)
    {
         IdentityResult rc = new IdentityResult();
    
        if ((userManager != null) && (userEmail != null) && (context != null) )
        {
            var user = await userManager.FindByEmailAsync(userEmail);
            var logins = user.Logins;
            var rolesForUser = await userManager.GetRolesAsync(user);
    
            using (var transaction = context.Database.BeginTransaction())
            {
              foreach (var login in logins.ToList())
              {
                await userManager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey);
              }
    
              if (rolesForUser.Count() > 0)
              {
                foreach (var item in rolesForUser.ToList())
                {
                  // item should be the name of the role
                  var result = await userManager.RemoveFromRoleAsync(user, item);
                }
              }
              rc = await userManager.DeleteAsync(user);
              transaction.Commit();
            }
        }
        return rc;
    }
    
  • 如果您使用的是最新版本的 ASP.NET,则不需要在视图中使用 @Html.AntiForgeryToken() 的 Brad 观点 - 请参阅仍需要 AntiForgeryToken
  • 为什么不为 AspNetUsers 创建一个 SQL 触发器,这样删除用户也会从 AspNetUserRoles 和 AspNetUserLogins 中删除用户的相应记录?
  • 我需要从多个地方调用 DeleteUser,所以我向 AccountController 添加了一个静态方法(见下文)。我仍在学习 MVC,所以应该感谢评论,特别是 1) 使用 IdentityResult 作为返回码 2) 以这种方式扩展 AccountController 的智慧 3) 将密码(明文)放入模型以验证操作(参见示例调用)。

     public static async Task<IdentityResult> DeleteUserAccount(UserManager<ApplicationUser> userManager, 
                                                                             string userEmail, ApplicationDbContext context)
    {
         IdentityResult rc = new IdentityResult();
    
        if ((userManager != null) && (userEmail != null) && (context != null) )
        {
            var user = await userManager.FindByEmailAsync(userEmail);
            var logins = user.Logins;
            var rolesForUser = await userManager.GetRolesAsync(user);
    
            using (var transaction = context.Database.BeginTransaction())
            {
              foreach (var login in logins.ToList())
              {
                await userManager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey);
              }
    
              if (rolesForUser.Count() > 0)
              {
                foreach (var item in rolesForUser.ToList())
                {
                  // item should be the name of the role
                  var result = await userManager.RemoveFromRoleAsync(user, item);
                }
              }
              rc = await userManager.DeleteAsync(user);
              transaction.Commit();
            }
        }
        return rc;
    }
    

Sample invocation - form passes the user's password (cleartext) in Model:

示例调用 - 表单在模型中传递用户的密码(明文):

        // POST: /Manage/DeleteUser
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> DeleteUser(DeleteUserViewModel account)
    {
        var user = await GetCurrentUserAsync();
        if ((user != null) && (user.PasswordHash != null) && (account != null) && (account.Password != null))
        {
            var hasher = new Microsoft.AspNetCore.Identity.PasswordHasher<ApplicationUser>();
            if(hasher.VerifyHashedPassword(user,user.PasswordHash, account.Password)  != PasswordVerificationResult.Failed)
            {
                IdentityResult rc = await AccountController.DeleteUserAccount( _userManager, user.Email, _Dbcontext); 
                if (rc.Succeeded)
                {
                    await _signInManager.SignOutAsync();
                    _logger.LogInformation(4, "User logged out.");
                    return RedirectToAction(nameof(HomeController.Index), "Home");
                }
            }
        }
        return View(account);
    }

回答by wpqs

Update for ASP.NET Core 2.0 - hope this saves someone a bit of time

ASP.NET Core 2.0 更新 - 希望这能节省一些时间

ApplicationDbContext context, 
UserManager<ApplicationUser> userManager, 
ApplicationUser user

var logins = await userManager.GetLoginsAsync(user);
var rolesForUser = await userManager.GetRolesAsync(user);

using (var transaction = context.Database.BeginTransaction())
{
    IdentityResult result = IdentityResult.Success;
    foreach (var login in logins)
    {
        result = await userManager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey);
        if (result != IdentityResult.Success)
            break;
    }
    if (result == IdentityResult.Success)
    {
        foreach (var item in rolesForUser)
        {
            result = await userManager.RemoveFromRoleAsync(user, item);
            if (result != IdentityResult.Success)
                break;
        }
    }
    if (result == IdentityResult.Success)
    {
        result = await userManager.DeleteAsync(user);
        if (result == IdentityResult.Success)
            transaction.Commit(); //only commit if user and all his logins/roles have been deleted  
    }
}

回答by Alik

I was looking also for the answer but finally this is what work well for me, even its old post but it may help for someone.

我也在寻找答案,但最终这对我来说很有效,即使是旧帖子,但它可能对某人有所帮助。

// GET: Users/Delete/5
    public ActionResult Delete(string id)
    {

        using (SqlConnection sqlCon = new SqlConnection(connectionString))
        {
            sqlCon.Open();

            string query = "DELETE FROM AspNetUsers WHERE Id = @Id";
            SqlCommand sqlCmd = new SqlCommand(query, sqlCon);
            sqlCmd.Parameters.AddWithValue("@Id", id);
            sqlCmd.ExecuteNonQuery();
        }

        return RedirectToAction("Index");
    }

    // POST: Users/Delete/5
    [HttpPost]
    public ActionResult Delete(string id, FormCollection collection)
    {
        try
        {
            // TODO: Add delete logic here

            return RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }