Welcome to Part 29 of our development series! Today we are kicking off the public-facing side of our application by building a high-performance, professional Product Details Page.
In Step 1, we implement the asynchronous GET Details action method. This method serves as the data foundation for the entire page, fetching a single product along with all of its relational data—such as images, specs, and user reviews—in a single database round-trip.
Here is the step-by-step breakdown of how this controller logic executes behind the scenes.
Part 29 — Step 1: Eager Loading with Entity Framework Core
The Data Hydration Architecture
Step-by-Step Code Explanation
Method Signature & Route Parameter
async Task<IActionResult>: Defines this method as an asynchronous action. By using non-blocking I/O operations, the underlying web server thread is released to handle other incoming user requests while waiting for SQL Server to return data.
int id: This parameter accepts the primary key of the product from the route URL (e.g.,
/Product/Details/5). ASP.NET Core automatically maps this via model binding.
Eager Loading via Include Expressions
.Include(p => p.Category)
.Include(p => p.Brand)
.Include(p => p.ProductImages)
.Include(p => p.Specifications)
By default, Entity Framework Core uses lazy loading or leaves navigation properties null. To prevent the notorious $N+1$ query performance bug when rendering a complex page, we use Eager Loading via the .Include() method.
This tells EF Core to generate SQL
LEFT JOINstatements immediately, pulling the lookup data forCategoryandBrand, the full image gallery array (ProductImages), and the technical spec sheets (Specifications) altogether.
Multi-Level Relationships (ThenInclude)
.ThenInclude(r => r.User)
.Include(p => p.Reviews): Grabs the collection of rating and review records associated with this specific product.
.ThenInclude(r => r.User): Moves down an additional layer into the object graph. It looks inside each individual
Reviewrecord and loads the corresponding identity profile of theUserwho wrote it. This allows us to display the reviewer's name alongside their rating on the front-end layout.
Asynchronous Execution Safeguard
This terminates our LINQ query stream. It scans the
Productstable looking for a record matching our passed route parameterid.Because it executes asynchronously via
FirstOrDefaultAsync(), it queries the database efficiently and returns either the fully hydratedProductobject or a standardnullreference if no ID matches.
Resource Validation & View Routing
return NotFound();
return View(product);
if (product == null): A quick validation check. If an end-user modifies the URL to point to a non-existent tracking ID, the server halts processing and gracefully outputs an HTTP 404 response.
return View(product): If found, the heavily packed
productmodel graph is forwarded right into our razor view template engine, where its fields are rendered onto our polished HTML storefront workspace.
n Step 2 of Part 29, we are moving to the front-end rendering layer to build a high-performance administrative Product Details View. This Razor template consumes our deep-loaded Product model graph and organizes it into a sleek, clean, multi-column layout using Bootstrap 5 and Font Awesome icons.
Here is the step-by-step breakdown of how this UI code renders your product data dynamically.
Step 2: Product Details Razor UI Implementation
The Interface Layout Architecture
Step-by-Step Layout Breakdown
Model Directive & Breadcrumb Navigation
...
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a asp-area="Admin" asp-controller="Dashboard" asp-action="Index">Dashboard</a></li>
<li class="breadcrumb-item"><a asp-action="Index">Products</a></li>
<li class="breadcrumb-item active">@Model.Name</li>
</ol>
@model Product: Strong-types our view instance to receive a singleProductobject.Breadcrumb Navigation: Implements an enterprise UI trail using anchor tag helpers (
asp-action,asp-controller). It outputs deep nested links back to your core management interfaces, mapping the final trailing active node to@Model.Name.
Left Column: Image Management Panel
{
<img src="@Model.MainImageUrl" class="img-fluid rounded mb-3 w-100" style="max-height: 300px; object-fit: contain;" />
}
else { ... }
Null Check Safeguard: Verifies whether the profile banner image contains a physical reference string. If empty, it drops into an alternate
elseblock containing a fallback gray placeholder box (bg-light).The Thumbnail Gallery:
@foreach (var img in Model.ProductImages.OrderBy(i => i.DisplayOrder))Iterates over the subsidiary image database entries we pulled using
.Include(). It automatically sorts them using our custom tracking weightDisplayOrderto build out an organized, evenly spaced structural grid.
Left Column: Badges & Real-Time Statistics
else if (Model.StockQuantity > 0) { <span class="badge bg-warning text-dark">Low Stock</span> }
else { <span class="badge bg-danger">Out of Stock</span> }
This snippet uses inline conditional Razor statement controls to check the product stock levels. It automatically transitions colors (
bg-success,bg-warning, orbg-danger) to give store managers instant visual context regarding inventory health.Star Rating System: A basic mathematical index loop iterates from 1 to 5, comparing the loop variable
iagainst@Model.AverageRatingto draw a solid gold star icon (fas fa-star) or an empty hollow star icon (far fa-star).
Right Column: Pricing & E-Commerce Flag Badges
@if (Model.OriginalPrice > Model.SalePrice)
{
<small class="text-muted text-decoration-line-through">RS @Model.OriginalPrice.ToString("N2")</small>
<span class="badge bg-danger ms-1">-@Model.DiscountPercentage%</span>
}
Prints the current running market value formatted as standard currency decimals using
.ToString("N2").If a markdown discount exists, it targets the old manufacturer retail values with a strikeout line layout rule (
text-decoration-line-through) and appends a bright promotional badge calculating the exact discount percentage remaining.
Technical Specifications: LINQ Grouping Records
var groupedSpecs = Model.Specifications.GroupBy(s => s.GroupName ?? "General").OrderBy(g => g.Key);
}
This leverages standard LINQ grouping right inside the markup canvas. It buckets custom metadata fields into neat corporate partitions based on their database assigned string identity values (like Screen, Battery, Processor). It loops through these categories, wrapping the key-value sets into sleek, responsive HTML info-tables.
Contextual Verification Controls & Reviews
Sorts incoming client reviews so the most recent submissions appear first, using .Take(5) to truncate the list and prevent long page scrolling. It also renders optional Verified Purchase tags and Pending Approval tracking status indicators for review moderation tasks.

Comments
Post a Comment