Welcome to Part 34 of our Mobile Shop development series! Today, we are diving into the User Roles Settings Setup.
In Step 1, we implement the Details Action Method inside your administrative user management controller. This method acts as a comprehensive customer profile inspector. When an administrator clicks to view a user, this action fetches their identity profile, dynamically pulls their assigned security permissions, tracks down their last 10 retail orders from your database, and prepares this composite view data for the frontend layout canvas.
Here is the step-by-step structural breakdown of how this profile retrieval method functions.
Step 1: User Profile & Order History Inspector
The Multi-Table Data Aggregation Pipeline
Step-by-Step Code Explanation
Identity Validation & Null-Guard Check
public async Task<IActionResult> Details(string id)
{
var user = await _userManager.FindByIdAsync(id);
if (user == null)
return NotFound();
FindByIdAsync(id): Uses your injected Microsoft IdentityUserManagerAPI to search the underlying core database strings for a matching string primary key identifier.Safety Guard: If the
idparameter is malformed, intentionally manipulated by a user, or doesn't exist, the system short-circuits instantly and drops a clean404 NotFound()HTTP response code, ensuring no null reference errors exception crashes occur down the page.
Permissions Mapping Query
var roles = await _userManager.GetRolesAsync(user);
GetRolesAsync(user): Instructs Identity to read across the many-to-many junction mapping table (AspNetUserRoles). It collects all text permission tags associated with this user profile (such as Admin, Customer, or Employee) into a flat string list collection.
Performance-Optimized Purchase Log Slicing
var orders = await _context.Orders
.Where(o => o.UserId == id)
.OrderByDescending(o => o.OrderDate)
.Take(10)
.ToListAsync();
This queries your primary e-commerce database entity context table mapping (
_context.Orders).Where(o => o.UserId == id): Screens the global purchase index table, isolating only the order records belonging explicitly to this customer.OrderByDescending(o => o.OrderDate): Chronologically sorts the database records, putting the newest receipts right at the top of the list.Take(10): This is an essential database performance best practice. If a loyal customer has placed hundreds of orders over multiple years, loading their entire transactional history onto a single summary card will slow down your server's database response. Limiting the pull to the most recent 10 transactions ensures the dashboard panel renders instantly.
Dynamic Packaging and Payload Delivery
ViewBag.Roles = roles;
ViewBag.Orders = orders;
return View(user);
Since a controller view can only accept a single object model data pass contract via
return View(user), we utilize the flexibleViewBagdata storage collection.This allows us to load the core
usermodel into the main page slot, while smoothly sliding the separateroleslist andordersdata array along the side of the request pipeline into your Razor view components.
In Step 2 of Part 34, we build out the front-end interface: the Details.cshtml view for User Roles and Settings.
This view functions as a 3-column administrative control cockpit. It displays complete customer data profile strings, handles shipping destination fields, loops through recent transactional orders using a C# switch pattern, and implements an interactive role management panel featuring inline POST forms to add or strip user access levels.
Step 2: Advanced User Profile UI Anatomy
The 3-Column Cockpit Layout Hierarchy
Step-by-Step UI Layout Explanation
Integrated Role-Management Form Architecture
<form asp-action="UpdateRole" method="post" class="d-inline">
<input type="hidden" name="id" value="@Model.Id" />
<input type="hidden" name="role" value="@role" />
<input type="hidden" name="add" value="false" />
<button type="submit" class="btn btn-sm btn-outline-danger">Remove</button>
</form>
}
Granular Role Revocation: The view loops through all roles currently held by the user. Next to each badge, it renders a specialized, self-contained
POSTconfiguration form.By explicitly passing
name="add" value="false", clicking "Remove" targets our upcoming backend action method to safely strip that exact access token away without affecting other assignments.
Additive Role Assignment Form
<input type="hidden" name="id" value="@Model.Id" />
<input type="hidden" name="add" value="true" />
<select name="role" class="form-select form-select-sm">...</select>
<button type="submit" class="btn btn-sm btn-success">Add</button>
</form>
Sitting directly below a thematic horizontal rule separator (
<hr />), this form passes a contrasting parameter (name="add" value="true"). When the administrator selects a tier from the dropdown list and hits "Add", it submits an intent to append that security tier to the profile.
Explicit Model Casting & Inline Switch Pattern
@foreach (var order in (List<Order>)ViewBag.Orders) {
<span class="badge @(order.Status switch {
OrderStatus.Pending => "bg-warning",
OrderStatus.Processing => "bg-info",
OrderStatus.Shipped => "bg-primary",
OrderStatus.Delivered => "bg-success",
OrderStatus.Cancelled => "bg-danger",
_ => "bg-secondary"
})">@order.Status</span>
}
Explicit C# Casting: Because objects inside
ViewBagare evaluated as dynamic types at runtime, we cast the payload explicitly using(List<Order>)ViewBag.Ordersso the Razor template can safely map properties inside our loops.The C# Switch Expression: We use a clean C# pattern inside our HTML class attribute wrapper. It evaluates the strongly typed
order.Statusenumeration value and returns a matching Bootstrap layout color helper class (e.g., yellow forPending, green forDelivered, and red forCancelled).
In Step 3 of Part 34, we implement the data-modification backend engine for our profile panel: the UpdateRole Action Method.
This method acts as the security processor for the two inline forms we built in the last step. It accepts the target user’s identifier, the role name string, and a boolean flag (add) determining the operation type. It then securely alters the user's security clearance mapping in the database before redirecting back to the profile cockpit.
Step 3: Role Modification Processing Workflow
The Role Assignment & Revocation Pipeline
Step-by-Step Code Explanation
Secure Form Routing Parameters
[HttpPost]
public async Task<IActionResult> UpdateRole(string id, string role, bool add)
[HttpPost]: Restricts this action to HTTP POST requests. Because modifying security permissions alters system behavior, blockingGETrequests prevents malicious trick URLs from changing user roles.The
addParameter: This boolean variable maps directly to the hidden inputs from our forms (value="true"orvalue="false"), allowing a single action method to gracefully handle both granting and revoking roles.
User Profile Isolation
var user = await _userManager.FindByIdAsync(id);
if (user == null)
return NotFound();
Searches your Identity framework table for the matching profile. If the ID is invalid or not found, it short-circuits the request and drops a clean
404 NotFound()warning page.
The Additive Permission Path (add == true)
if (add)
{
if (!await _userManager.IsInRoleAsync(user, role))
{
await _userManager.AddToRoleAsync(user, role);
TempData["Success"] = $"Role '{role}' added to user.";
}
}
Idempotency Guard (
IsInRoleAsync): Before adding a role, the system checks if the user already holds it. This prevents database duplicate-key crashes if an administrator opens multiple tabs and double-clicks the "Add" button.AddToRoleAsync(user, role): Instructs Identity to safely insert a fresh relational mapping row linking this user's primary key to the target security role index.
The Subtractive Revocation Path (add == false)
else
{
if (await _userManager.IsInRoleAsync(user, role))
{
await _userManager.RemoveFromRoleAsync(user, role);
TempData["Success"] = $"Role '{role}' removed from user.";
}
}
If
addevaluates tofalse, execution drops down into theelseblock to handle role removal.It verifies the user currently holds that role, and then triggers
RemoveFromRoleAsync(user, role)to delete that specific mapping row from the database table.
Dashboard Navigation Loop Synchronization
return RedirectToAction(nameof(Details), new { id });
new { id }Route Values: To refresh the screen correctly after processing the change, the controller redirects back to ourDetailsinspector view. Because theDetailsmethod requires a route ID parameter to load a profile, we pass an anonymous route values object container (new { id }) containing our current user's identification string.

Comments
Post a Comment