Start with a view-model:
public class UserViewModel
{
public int Id { get; set; }
public string EmailAddress { get; set; }
public string Username { get; set; }
public List<UserRoleViewModel> Roles { get; set; }
}
public class UserRoleViewModel
{
public int RoleId { get; set; }
public string RoleName { get; set; }
public bool IsSelected { get; set; }
}
In the controller, you will need to populate the view-model:
private void PopulateRoles(UserViewModel model)
{
DataTable allRoles = ListAllRolesFromDatabase();
var roles = allRoles.AsEnumerable()
.Select(r => new UserRoleViewModel
{
RoleId = r.Field<int>("Id"),
RoleName = r.Field<string>("Name")
})
.ToDictionary(r => r.RoleId);
if (model.Roles != null)
{
foreach (var role in model.Roles.Where(r => r.IsSelected))
{
if (roles.TryGetValue(role.RoleId, out var vm))
{
vm.IsSelected = true;
}
}
}
else if (model.Id != 0)
{
DataTable currentRoles = LoadRolesForUserFromDatabase(model.Id);
foreach (DataRow row in currentRoles.Rows)
{
int id = row.Field<int>("Id");
if (roles.TryGetValue(id, out var vm))
{
vm.IsSelected = true;
}
}
}
model.Roles = roles.Values.ToList();
}
Call that method before you display the add/edit view:
[HttpGet]
public ActionResult Add()
{
var model = new UserViewModel();
PopulateRoles(model);
return View(model);
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Add(UserViewModel model)
{
if (!ModelState.IsValid)
{
PopulateRoles(model);
return View(model);
}
CreateUserAndAssignRolesInDatabase(model);
return RedirectToAction(nameof(Index));
}
[HttpGet]
public ActionResult Edit(int id)
{
DataRow row = LoadUserDetailsFromDatabase(id);
if (row is null) return RedirectToAction(nameof(Index));
var model = new UserViewModel
{
Id = row.Field<int>("Id"),
EmailAddress = row.Field<string>("Email"),
Username = row.Field<string>("Username"),
};
PopulateRoles(model);
return View(model);
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Edit(UserViewModel model)
{
if (!ModelState.IsValid)
{
PopulateRoles(model);
return View(model);
}
UpdateUserAndRolesInDatabase(model);
return RedirectToAction(nameof(Index));
}
Then in your add/edit view, render a checkbox list for the roles - I'm using Bootstrap 4 here:
<div class="form-group row">
<label class="col-form-label col-sm-3">Roles</label>
<div class="col-sm">
<ul class="list-unstyled">
@for (int index = 0; index < Model.Roles.Count; index++)
{
<li>
<div class="form-check mt-2">
@Html.HiddenFor(m => m.Roles[index].RoleId)
@Html.CheckBoxFor(m => m.Roles[index].IsSelected, new { @class = "form-check-input" })
@Html.LabelFor(m => m.Roles[index].IsSelected, Model.Roles[index].RoleName)
</div>
</li>
}
</ul>
</div>
</div>
Now you just need to write the methods to load and save the view-model data to the database. Since you've already got the display working, I'll assume you already know how to do that. :)