Welcome to Part 25 of our series! Now that we have fully completed our Category Management module, we are moving on to the heart of our e-commerce platform: Product Management.
In Step 1, we are going to create the administrative command center for our inventory—the ProductsController—inside the Admin Area.
Step 1: Secure Product Management Scaffolding
Just like we did for categories, we need to establish a secure, isolated controller structure before we write our CRUD action methods. This controller will handle all administrative product tasks such as checking stock, uploading product images, setting prices, and assigning products to categories.
The Initial Step 1 Code Structure
Create a new controller named ProductsController.cs inside your Areas/Admin/Controllers/
Architectural Blueprint Explained
Area Isolation (
[Area("Admin")]): This attribute tells the framework's routing engine that this controller belongs specifically to the administrative ecosystem. It separates your product catalog management endpoints from public-facing customer store routes.Role-Based Access Control (
[Authorize(Roles = "Admin")]): Products hold pricing, profit margins, and cost data. By enforcing this class-level guardrail, the system blocks unauthorized users or malicious bots from tampering with your store inventory or executing unintended updates.Data Context Dependency Injection: By declaring a
private readonly ApplicationDbContext _contextand initializing it right through the constructor, we follow clean architectural design principles. This injects our database context safely, giving every future action method a direct, secure channel to read and write product information.
Step 2: Multi-Filter Search and Pagination Pipeline
This index method combines multiple incoming search criteria and processes them inside SQL Server using deferred execution before returning a specific slice of data to the view.
Core Logic & LINQ Processing Explained
Eager Loading Relational Data (
.Include):var query = _context.Products.Include(p => p.Category).Include(p => p.Brand).AsQueryable();By default, Entity Framework will not fetch linked relational tables. We use
.Include()to tell EF Core to write SQLJOINstatements. This fetches the Category Name and Brand Name alongside each product in a single database round-trip, completely avoiding the costly "N+1 query performance problem.".AsQueryable()keeps this query open for further modifications.Dynamic Query Accumulation (Deferred Execution): Notice that our search, category, and brand filters are wrapped in conditional
ifblocks. Becausequeryis anIQueryable, code likequery.Where(...)does not run against the database immediately. It appends criteria to an internal SQL script builder, optimizing the query before execution.Server-Side Pagination Math (
.Skipand.Take):.Skip((page - 1) * pageSize).Take(pageSize)Instead of pulling 10,000 products into web server memory and slicing them on the screen, pagination slices records directly inside the database engine:
CountAsync(): Determines the total matching records to calculate the layout pagination buttons.Skip(): Drops previous pages' records (e.g., if on page 3, it skips(3 - 1) * 20 = 40products).Take(): Grabs exactly thepageSizelimit (20 products) for the active display.
View Layout Context Delivery (
ViewBag): To construct drop-down search selectors in the frontend view, we fetch list collections for active Categories and Brands, sending them throughViewBagalongside the calculated tracking values likeCurrentPageandTotalPages.
Step 3: Product Inventory Filtering & Display Interface
This design balances massive data transparency with clean, context-aware interface elements to make warehouse management completely foolproof.
Core UI Mechanics & Layout Features Explained
GET-Based Search Form (
method="get"): Unlike your creation data panels that usePOSTactions to save data, our search form usesmethod="get". This ensures that all filter parameters (search keyword, category ID, and brand ID) append cleanly to the browser URL bar like an address line (?search=iPhone&categoryId=3). This lets administrators bookmark specific filtered pages or share them with other team members.Null-Safe Image Fallbacks:
<img src="@(product.MainImageUrl ?? "https://via.placeholder.com/50x50?text=No+Image")" />If a new mobile phone record is added before the creative design team uploads the photo asset, the C# null-coalescing operator (
??) automatically catches the blank data string. It falls back to a clean placeholder image, keeping your text column alignments square.Dynamic Markdown Retail Price Calculations (
<del>):@if (product.OriginalPrice > product.SalePrice) {<del class="text-muted small">RS @product.OriginalPrice.ToString("N0")</del> }The view executes a live mathematical check on price fields. If an item is on sale, it renders the original cost with a standard HTML strike-through tag (
<del>), placing the activeSalePricedirectly underneath it formatted using the"N0"currency thousands-separator layout.Low-Stock Alert Threshold Visual Badges: To speed up re-ordering times, a conditional structural block monitors incoming inventory counts:
bg-danger: Triggers a bright red indicator bubble when inventory drops to 10 items or lower to signal an immediate re-order state.bg-success: Displays a calm green indicator bubble when warehouse stocks are secure.
Sticky Parameter Multi-Page Pagination Layout:
asp-route-page="@i" asp-route-search="@ViewBag.Search"Standard pagination components often lose tracking variables when an admin clicks from page 1 to page 2. This structure carries your state variables forward by explicitly packing
@ViewBag.Searchparameters into the Razor anchor routing tags, keeping filter restrictions locked as you navigate between data pages.

Comments
Post a Comment