ASP .NET Core 3.1 - Usando o Identity de cabo a rabo - XVI


Hoje vamos continuar a série de artigos mostrando como usar o ASP .NET Core Identiy na versão 3.1 da ASP .NET .Core e do EF Core.

Continuando a décima quinta parte do artigo veremos como adicionar e remover usuários de uma role.

Incluindo e removendo usuários de uma role

Na view EditRole que criamos na aula anterior definimos o botão para remover e/ou adicionar um usuário para a role selecionada.

Quando o usuário clicar no botão Incluir/Remover usuário vamos exibir a lista de usuários existentes na role com um checkbox, e os botões para Atualizar ou Cancelar a operação.

Se você quiser que o usuário seja membro da role basta marcar o checkbox e clicar no botão Atualizar que a tabela AspNetUserRoles do banco de dados será atualizada. Esta tabela esta relacionada com as tabelas AspNetUsers e AspNetRoles.

Assim vamos entender como funciona o relacionamento entre as tabelas AspNetUsers, AspNetUserRoles e AspNetRoles.

Os usuários da aplicação são armazenados na tabela AspNetUsers, as roles são armazenadas na tabela AspNetRoles, e, na tabela AspNetUserRoles temos o mapeamento entre os usuários e as suas roles.

O relacionamento entre as tabelas é feito pelo Id do usuário com a coluna UserId e a coluna RoleId com o Id da role:

Assim existe um relacionamento muitos-para-muitos entre as tabelas, e, a tabela AspNetUserRoles é a tabela de junção:

Neste relacionamento muitos-para-muitos um usuário pode ser um membro de várias roles e uma role pode conter muitos usuários como membros.

As duas colunas da tabela AspNetUserRoles: UserId e RoleId são ambas chaves estrangeiras.

Precisamos expressar esse relacionamento criando a viewmodel UserRoleViewModel na pasta ViewModels:

public class UserRoleViewModel
{
        public string UserId { get; set; }
        public string UserName { get; set; }
        public bool IsSelected { get; set; }
}

Definimos nesta classe as propriedade UserId, UserName e IsSelected. Onde:

Poderíamos incluir a propriedade RoleId também na classe UserRoleViewModel, mas no que diz respeito a essa view, existe um relacionamento de um para muitos de Role para Users. Portanto, para não repetir o RoleId para cada usuário, usaremos o recurso ViewBag para passar o RoleId do controlador para a view.

Agora podemos criar o método Action EditUsersInRole() que vai implementar a inclusão e remoção de um usuário na role. Vamos começar com o método HttpGet:

[HttpGet]
public async Task<IActionResult> EditUsersInRole(string roleId)
{
    ViewBag.roleId = roleId;
    var role = await roleManager.FindByIdAsync(roleId);
    if (role == null)
    {
        ViewBag.ErrorMessage = $"Role com Id = {roleId} não foi encontrada";
        return View("NotFound");
    }
    var model = new List<UserRoleViewModel>();
    var listaUsuarios = userManager.Users.ToList();
    foreach (var user in listaUsuarios)
    {
        var userRoleViewModel = new UserRoleViewModel
        {
            UserId = user.Id,
            UserName = user.UserName
        };
        if (await userManager.IsInRoleAsync(user, role.Name))
        {
            userRoleViewModel.IsSelected = true;
        }
        else
        {
            userRoleViewModel.IsSelected = false;
        }
        model.Add(userRoleViewModel);
    }
    return View(model);
}

No código deste método Action estamos recebendo o ID da role via URL e a seguir armazenamos em um ViewBag.

Localizamos a role pelo seu ID, e, se ela não for encontrada retornamos um NotFound.

Listamos os usuários existentes na role e usando o método IsInRoleAsycn(user,role.Name) verificamos se o usuário pertence ou não à role e atribuímos o valor true ao checkbox caso o usuário pertença à role.

A seguir o método Action EditUsersInRole HttpPost:

        [HttpPost]
        public async Task<IActionResult> EditUsersInRole(List<UserRoleViewModel> model, string roleId)
        {
            var role = await roleManager.FindByIdAsync(roleId);
            if (role == null)
            {
                ViewBag.ErrorMessage = $"Role com Id = {roleId} não foi encontrada";
                return View("NotFound");
            }
            for (int i = 0; i < model.Count; i++)
            {
                var user = await userManager.FindByIdAsync(model[i].UserId);
                IdentityResult result = null;
                if (model[i].IsSelected && !(await userManager.IsInRoleAsync(user, role.Name)))
                {
                    result = await userManager.AddToRoleAsync(user, role.Name);
                }
                else if (!model[i].IsSelected && await userManager.IsInRoleAsync(user, role.Name))
                {
                    result = await userManager.RemoveFromRoleAsync(user, role.Name);
                }
                else
                {
                    continue;
                }
                if (result.Succeeded)
                {
                    if (i < (model.Count - 1))
                        continue;
                    else
                        return RedirectToAction("EditRole", new { Id = roleId });
                }
            }
            return RedirectToAction("EditRole", new { Id = roleId });
        }

No método HttpPost da Action usamos o método IsInRoleAsync() para verificar se o usuário pertence à role,  o método AddToRoleAsync() para incluir o usuário na role e o método RemoveFromRoleAsync() para remover o usuário da role. Esses métodos são da classe UserManager.

Para concluir vamos criar a view EditUsersInRole:

@model List<UserRoleViewModel>
@{
    var roleId = ViewBag.roleId;
}
<form method="post">
    <div class="card">
        <div class="card-header">
            <h2>Incluir ou remover usuários desta role</h2>
        </div>
        <div class="card-body">
            @for (int i = 0; i < Model.Count; i++)
            {
                <div class="form-check m-1">
                    <input type="hidden" asp-for="@Model[i].UserId" />
                    <input type="hidden" asp-for="@Model[i].UserName" />
                    <input asp-for="@Model[i].IsSelected" class="form-check-input" />
                    <label class="form-check-label" asp-for="@Model[i].IsSelected">
                        @Model[i].UserName
                    </label>
                </div>
            }
        </div>
        <div class="card-footer">
            <input type="submit" value="Atualizar" class="btn btn-primary" style="width:auto" />
            <a asp-action="EditRole" asp-route-id="@roleId"
               class="btn btn-primary" style="width:auto">Cancelar</a>
        </div>
    </div>
</form>

Agora precisamos ajustar o código da view EditRole e definir no botão Incluir/Remover Usuário a chamada ao método Action EditUsersInRole no controlador Administration passando o ID da role :

...

 <div class="card-footer">
      <a asp-controller="Administration" asp-action="EditUsersInRole"
           asp-route-roleId="@Model.Id" class="btn btn-primary">
            Incluir/Remover Usuário da Role
     </a>
</div>

...

Vamos executar o projeto e navegar para http://localhost:xxxx/administration/listroles para obter a lista de roles:

Agora vamos clicar no botão Editar para exibir os dados da role Admin selecionada pelo seu ID para edição:

Agora clique no botão Incluir/Remover Usuário :

Note que é passado o ID a role na URL e é exibido o usuário existente.

Agora podemos incluir esse usuário na role Admin marcando o checkbox e clicando no botão Atualizar.

Agora vemos que o usuário faz parte da role Admin e esta sendo exibido na view EditRole.

No próximo artigo vamos implementar a exclusão de uma role existente.

Pegue o projeto aqui: IdentityTotal_Roles_Users.zip (sem as referências)

"Sede unânimes entre vós; não ambicioneis coisas altas, mas acomodai-vos às humildes; não sejais sábios em vós mesmos;"
Romanos 12:16

Referências:


José Carlos Macoratti