I would like to create a Razor Page which lists all users with their associated roles. While I could find some tutorials doing this for previous versions of ASP.NET MVC but I am not able to do this using ASP.NET Core 6.0 Identity Razor pages. Currently, I have the following code but I get an error message when navigating to the page
//UserRolesViewModel.cs file
namespace MyApp.Models
{
public class UserRolesViewModel
{
public string UserName { get; set; }
public string Email { get; set; }
public IEnumerable<string> Roles { get; set; }
}
}
//Index.cshtml.cs file
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using MyApp.Models;
namespace MyApp.Pages.Users
{
public class IndexModel : PageModel
{
private readonly UserManager<IdentityUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
public IndexModel
(
UserManager<IdentityUser> userManager,
RoleManager<IdentityRole> roleManager
)
{
_userManager = userManager;
_roleManager = roleManager;
}
public IList<UserRolesViewModel> UserRolesViewModel { get; set; }
public async Task<IActionResult> OnGetAsync()
{
var users = await _userManager.Users.ToListAsync();
var userRolesViewModel = new List<UserRolesViewModel>();
foreach (IdentityUser user in users)
{
var thisViewModel = new UserRolesViewModel();
thisViewModel.Email = user.Email;
thisViewModel.Roles = await GetUserRoles(user);
userRolesViewModel.Add(thisViewModel);
}
return Page();
}
private async Task<List<string>> GetUserRoles(IdentityUser user)
{
return new List<string>(await _userManager.GetRolesAsync(user));
}
}
}
//Index.cshtml file
@page
@model MyApp.Pages.Users.IndexModel
@{
ViewData["Title"] = "Index";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>Index</h1>
<p>
<a asp-page="Create">Create New</a>
</p>
<table >
<thead>
<tr>
<th>
User
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.UserRolesViewModel) {
<tr>
@Html.DisplayFor(modelItem => item.Email)
</tr>
<td>@string.Join(" , ", item.Roles.ToList())</td>
}
</tbody>
</table>
//Error message
NullReferenceException: Object reference not set to an instance of an object.
MyApp.Pages.Users.Pages_Users_Index.ExecuteAsync() in Index.cshtml
@foreach (var item in Model.UserRolesViewModel)
CodePudding user response:
Use Any() function to check if the model holds data or you can add a spinner.
Try this out.
@page
@model MyApp.Pages.Users.IndexModel
@{
ViewData["Title"] = "Index";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>Index</h1>
<p>
<a asp-page="Create">Create New</a>
</p>
if(Model.Any())
{
<table >
.
.rest of the code
.
.</table>
}
another solution is to add a spinner while the page loads the data like this :
@{
ViewData["Title"] = "Index";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>Index</h1>
<p>
<a asp-page="Create">Create New</a>
</p>
if(Model == null)
{
<div class = "spinner">
</div>
}
else{
<table >
.
.rest of the code
.
.</table>
}
please note that in the second solution you need to add spinner to your css
CodePudding user response:
I write a simple demo here, you can refer to it
[BindProperty]
public IList<UserRolesViewModel> model { get; set; } = new List<UserRolesViewModel>();
public class UserRolesViewModel
{
public string UserName { get; set; }
public string Email { get; set; }
public IEnumerable<string> Roles { get; set; }
}
public async Task<IActionResult> OnGetAsync()
{
var users = await _userManager.Users.ToListAsync();
foreach (IdentityUser user in users)
{
UserRolesViewModel urv = new UserRolesViewModel()
{
UserName = user.UserName,
Email = user.Email,
Roles = await _userManager.GetRolesAsync(user)
};
model.Add(urv);
}
return Page();
}
page
@page
@model IndexModel
@foreach (var item in Model.model)
{
<div>User: @item.UserName</div>
<div>Email: @item.Email</div>
@foreach (var role in item.Roles)
{
<div>Role: @role</div>
}
<br />
}
result