昨日、ロールのCRUD機能がすべて完了したので、次にロールをユーザーに割り当てます。まずViewModel CustomUserRoleViewModelを作成します。これはロールに属するユーザーを表示するためのViewModelです。
namespace BlazorServer.ViewModels;
public class CustomUserRoleViewModel
{
public string? UserId { get; set; }
public string? UserName { get; set; }
public bool IsSelected { get; set; }
}
次にIRolesRepositoryとRolesRepositoryに、ロール内のユーザーを編集する機能を追加しました。ここではオーバーロードを使用しており、最初のメソッドはGet(ページの初期データを取得)に相当し、2番目のメソッドはPost(変更後のデータを送信)に相当します。
IRolesRepository.cs
…
Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string roleId);
Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string roleId);
RolesRepository.cs
…
public async Task<List<CustomUserRoleViewModel>> EditUsersInRoleAsync(string roleId)
{
var role = await _roleManager.FindByIdAsync(roleId);
var model = new List<CustomUserRoleViewModel>();
// ここで注意:_userManager.Users.ToList()の後には必ず.ToList()を付けてください。そうしないと例外がスローされます:https://stackoverflow.com/questions/60727080/helping-solving-there-is-already-an-open-datareader-associated-with-this-comman
foreach (var user in _userManager.Users.ToList())
{
var userRoleViewModel = new CustomUserRoleViewModel
{
UserId = user.Id,
UserName = user.UserName
};
if (await _userManager.IsInRoleAsync(user, role.Name))
userRoleViewModel.IsSelected = true;
else
userRoleViewModel.IsSelected = false;
model.Add(userRoleViewModel);
}
return model;
}
public async Task<ResultViewModel> EditUsersInRoleAsync(List<CustomUserRoleViewModel> model, string roleId)
{
var role = await _roleManager.FindByIdAsync(roleId);
foreach (var m in model)
{
var user = await _userManager.FindByIdAsync(m.UserId);
IdentityResult result;
if (m.IsSelected && !await _userManager.IsInRoleAsync(user, role.Name))
result = await _userManager.AddToRoleAsync(user, role.Name);
else if (!m.IsSelected && await _userManager.IsInRoleAsync(user, role.Name))
result = await _userManager.RemoveFromRoleAsync(user, role.Name);
else
continue;
if (result.Succeeded)
{
if (model.Count > 0)
continue;
return new ResultViewModel
{
Message = roleId,
IsSuccess = true
};
}
return new ResultViewModel
{
Message = roleId,
IsSuccess = false
};
}
return new ResultViewModel
{
Message = roleId,
IsSuccess = true
};
}
次にページを追加します。
EditUsersInRole.razor.cs
using BlazorServer.Repository;
using BlazorServer.Shared;
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace BlazorServer.Pages.RolesManagement;
public partial class EditUsersInRole
{
[Inject] protected IRolesRepository? RolesRepository { get; set; }
[Inject] protected NavigationManager? NavigationManager { get; set; }
[Inject] protected IJSRuntime? Js { get; set; }
private JsInteropClasses? _jsClass;
[Parameter] public string? Id { get; set; }
public List<CustomUserRoleViewModel> UserRoleViewModel { get; set; } = new List<CustomUserRoleViewModel>();
protected override async Task OnInitializedAsync()
{
await LoadData();
_jsClass = new JsInteropClasses(Js!);
}
private async Task LoadData()
{
UserRoleViewModel = (await RolesRepository!.EditUsersInRoleAsync(Id!)).ToList();
}
public async Task HandleValidSubmit()
{
var result = await RolesRepository!.EditUsersInRoleAsync(UserRoleViewModel, Id!);
if (result.IsSuccess)
{
NavigationManager!.NavigateTo($"/RolesManagement/EditRole/{Id}");
}
else
{
await _jsClass!.Alert(result.Message!);
}
}
public void Cancel()
{
NavigationManager!.NavigateTo($"/RolesManagement/EditRole/{Id}");
}
}
EditUsersInRole.razor
@page "/RolesManagement/EditUsersInRole/{Id}" @attribute [Authorize]
<EditForm Model="UserRoleViewModel" OnValidSubmit="HandleValidSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="card">
<div class="card-header">
<h2>ロールにユーザーを追加または削除する</h2>
</div>
<div class="card-body">
@foreach (var user in UserRoleViewModel) {
<div class="form-check m-1">
<label class="form-check-label">
<InputCheckbox @bind-Value="@user.IsSelected"></InputCheckbox>
@user.UserName
</label>
</div>
}
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary">更新</button>
<button type="button" class="btn btn-danger" @onclick="@Cancel">
キャンセル
</button>
</div>
</div>
</EditForm>
EditRole.razor.csにメソッドを追加し、EditUsersInRole.razorへ遷移することで、ロールに属するユーザーを編集できるようにします。
public void EditUsersInRole()
{
NavigationManager!.NavigateTo($"/RolesManagement/EditUsersInRole/{Id}");
}
EditRole.razorにボタンを追加し、このメソッドを呼び出せるようにします。
<button type="button" class="btn btn-info" @onclick="EditUsersInRole">
このロールに属するユーザーを追加または削除する
</button>

Webアプリを起動し、ロール編集ユーザーページに移動すると、現在1人のユーザーのみが表示されます。このユーザーにAdminロールを割り当てます。これでAdminとtest@gmail.comの割り当てが完了しました。ただし、成功したかどうかをテストするには、ページに制限を追加し、さらにテスト用のユーザーを追加する必要があります。

まず、各razor componentの@attribute [Authorize]を@attribute [Authorize(Roles = "Admin")]に変更します。これにより、このページを表示できるのはAdminロールを持つユーザーのみとなります。
次にNavMenu.razorに移動し、既存の<AuthorizeView>の上に以下のコードを追加します。同様に、Rolesリンクを表示できるのはAdminロールを持つユーザーのみとし、元の<AuthorizeView>内のRolesリンクは削除します。
<AuthorizeView Roles="Admin">
<Authorized>
<li class="nav-item px-3">
<NavLink
class="nav-link"
href="RolesManagement/RolesList"
Match="NavLinkMatch.All"
>
<span class="bi bi-kanban-fill h4 p-2 mb-0" aria-hidden="true"></span>
Roles
</NavLink>
</li>
</Authorized>
</AuthorizeView>
最後に、user@gmail.comという名前のユーザーを新規作成します。このユーザーでログインすると、左側のRolesが表示されなくなり、URLを直接入力してもアクセス権がないというエラーが表示されます。これが最も基本的なロール(Role)認可です。システムが非常にシンプルで、ロールだけで権限を分割するだけで十分であれば、この方法で要件を満たせます。


参照:
注:本記事のコードは .NET 6 + Visual Studio 2022 でリファクタリングされています。原文リンクをクリックしてリファクタリング後のコードと比較しながら学習できます。ご清読ありがとうございます。原作者をサポートしてください。