Saturday, September 7, 2019

Add or remove users from role in asp.net core

We will discuss how to manage role membership i.e add or remove users from a given role using the asp.net core identity api.

On Edit role view, when Add or remove users from this role button is clicked, we want to redirect to Edit users in role view


asp.net core identity add user to role

<div class="card-footer">
    <a asp-controller="Administration" asp-action="EditUsersInRole"
       asp-route-roleId="@Model.Id" class="btn btn-primary">
        Add or remove users from this role
    </a>
</div>

Edit users in role view is as shown below. If you want the user to be a member of the given role, check the checkbox, otherwise leave it unchecked.

asp.net core identity remove user from role

Once the Update button is clicked the data should be updated in the underlying AspNetUserRoles database table.

AspNetUserRoles identity database table

Application users are stored in AspNetUsers database table, where as roles are stored in AspNetRoles table. UserRoles i.e user to role mapping data is stored in AspNetUserRoles table.

asp.net core aspnetuserroles table

There is a Many-to-Many relationship between AspNetUsers and AspNetRoles table. A user can be a member of many roles and a role can contain many users as it's members. This User and Role mapping data is stored in AspNetUserRoles table.

This table has just 2 columns - UserId and RoleId. Both are foreign keys. UserId column references Id column in AspNetUsers table and RoleId column references Id column in AspNetRoles table.

UserRoleViewModel Class

public class UserRoleViewModel
{
    public string UserId { getset; }
    public string UserName { getset; }
    public bool IsSelected { getset; }
}
  • In the UserRoleViewModel class, in addition to UserId property, we have UserName and IsSelected properties. 
  • UserName property is required so we can display the UserName on the view. 
  • IsSelected property is required to determine if the user is selected to be a member of the role.
  • We could include RoleId property also in the UserRoleViewModel class, but as far as this view is concerned, there is a one-to-many relationship from Role to Users. So, in order not to repeat RoleId for each User, we will use ViewBag to pass RoleId from controller to the view.
HttpGet EditUsersInRole Action

[HttpGet]
public async Task<IActionResult> EditUsersInRole(string roleId)
{
    ViewBag.roleId = roleId;

    var role = await roleManager.FindByIdAsync(roleId);

    if (role == null)
    {
        ViewBag.ErrorMessage = $"Role with Id = {roleId} cannot be found";
        return View("NotFound");
    }

    var model = new List<UserRoleViewModel>();

    foreach (var user in userManager.Users)
    {
        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);
}

HttpPost EditUsersInRole Action

[HttpPost]
public async Task<IActionResult> EditUsersInRole(List<UserRoleViewModel> model, string roleId)
{
    var role = await roleManager.FindByIdAsync(roleId);

    if (role == null)
    {
        ViewBag.ErrorMessage = $"Role with Id = {roleId} cannot be found";
        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 });
}

EditUsersInRole View

@model List<UserRoleViewModel>

@{
    var roleId = ViewBag.roleId;
}

<form method="post">
    <div class="card">
        <div class="card-header">
            <h2>Add or remove users from this 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="Update" class="btn btn-primary"
                   style="width:auto" />
            <a asp-action="EditRole" asp-route-id="@roleId"
               class="btn btn-primary" style="width:auto">Cancel</a>
        </div>
    </div>
</form>


No comments:

Post a Comment

How to register multiple implementations of the same interface in Asp.Net Core?

 Problem: I have services that are derived from the same interface. public interface IService { } public class ServiceA : IService { ...