Introduction
Welcome back to the Mobile Shop Website development series. In Part 4, we are moving beyond the basic setup to focus on the heart of our application: the Home Page Settings. A professional e-commerce site needs a clean, responsive, and dynamic landing page. In this tutorial, we will configure the Home Page UI, set up the layout, and ensure our ASP.NET Core MVC project is ready for product displays.
Step 1: Implementing the Product Service Pattern
In this section, we move the business logic from the Controller to a dedicated Service Layer. This is a Best Practice in ASP.NET Core Architecture, as it keeps our controllers thin and our code reusable.
Why use an Interface and Service?
By creating IProductService, we define a contract for our application. This allows us to fetch dynamic data for our Mobile Shop Home Page—such as Featured Products, New Arrivals, and Bestsellers—efficiently using Entity Framework Core.
The Code: IProductService.cs
Create a new folder named Services in your project root, then create a file named IProductService.cs and paste the following code:
using Microsoft.EntityFrameworkCore;
using MobileShop.Data;
using MobileShop.Models;
namespace MobileShop.Services
{
public interface IProductService
{
Task<List<Product>> GetFeaturedProductsAsync(int count = 8);
Task<List<Product>> GetNewArrivalsAsync(int count = 8);
Task<List<Product>> GetBestsellersAsync(int count = 8);
Task<List<Brand>> GetBrandsAsync(int count = 6);
}
public class ProductService : IProductService
{
private readonly ApplicationDbContext _context;
public ProductService(ApplicationDbContext context)
{
_context = context;
}
public async Task<List<Product>> GetBestsellersAsync(int count = 8)
{
return await _context.Products
.Include(p => p.Brand)
.Include(p => p.Reviews)
.Where(p => p.IsBestseller && p.IsActive)
.Take(count)
.ToListAsync();
}
public async Task<List<Product>> GetFeaturedProductsAsync(int count = 8)
{
return await _context.Products
.Include(p => p.Brand)
.Include(p => p.Reviews)
.Where(p => p.IsFeatured && p.IsActive)
.Take(count)
.ToListAsync();
}
public async Task<List<Product>> GetNewArrivalsAsync(int count = 8)
{
return await _context.Products
.Include(p => p.Brand)
.Include(p => p.Reviews)
.Where(p => p.IsNewArrival && p.IsActive)
.OrderByDescending(p => p.CreatedAt)
.Take(count)
.ToListAsync();
}
public async Task<List<Brand>> GetBrandsAsync(int count = 6)
{
return await _context.Brands.Where(b => b.IsActive == true).Take(count).ToListAsync();
}
}
}
Detailed Code Explanation
To understand how this service powers our Mobile Shop Website, let’s break down the key technical components:
1. Asynchronous Programming (async/await)
We use Task<List<...>> and async/await to ensure our application remains responsive. In modern web development, Asynchronous Programming in ASP.NET Core is essential for handling multiple user requests without blocking the server threads.
2. Eager Loading with .Include()
Notice the use of .Include(p => p.Brand). This is known as Eager Loading in Entity Framework Core. It ensures that when we fetch a product, its related Brand and Review data are loaded in a single database query, which improves performance and avoids the "N+1 query" problem.
3. LINQ Filtering for E-commerce
We are using LINQ (Language Integrated Query) to filter our data:
IsActive: Ensures only enabled products are shown to customers.
OrderByDescending: Used in
GetNewArrivalsAsyncto show the most recently added mobile phones first.Take(count): Limits the number of items returned, which is perfect for designing a clean, high-speed Home Page UI.
4. Dependency Injection Readiness
The ProductService constructor accepts ApplicationDbContext. This is a classic example of Dependency Injection (DI). By injecting the database context, we make our service more modular and easier to maintain.
In the next part we will register this service in Program.cs and use it in our Home Controller to display these products on the frontend!</List<...>
Step 2: Registering the Product Service in Program.cs
Once the service and interface are created, we must tell the ASP.NET Core framework how to handle them. This is done in the Program.cs file, which is the entry point of our application.
The Code: Service Registration
Open your Program.cs file, find the area where services are being added (before builder.Build()), and add the following line:
// Register Custom Services for Dependency Injection
builder.Services.AddScoped<IProductService, MobileShop.Services.ProductService>();
Detailed Explanation
To build a high-performance Mobile Shop Website, understanding service lifetimes is essential. Here is why we use this specific code:
1. Understanding Dependency Injection (DI)
Dependency Injection is a design pattern that allows us to develop loosely coupled code. Instead of manually creating an object using new ProductService(), we ask the system to "inject" it. This makes our application easier to scale, test, and maintain.
2. Why AddScoped? (Service Lifetimes)
In ASP.NET Core, we have three main service lifetimes. For our Product Service, we use AddScoped:
AddScoped: This creates a new instance of the service for every HTTP Request. Once the request is finished (e.g., the page is loaded), the instance is disposed of. This is the best practice for database-related services like ours because it ensures the
ApplicationDbContextis used efficiently within a single user session.AddTransient: Creates a new instance every time the service is requested.
AddSingleton: Creates one single instance for the entire lifetime of the application.
3. Mapping Interface to Implementation
The syntax AddScoped<IProductService, ProductService>() tells the container:
"Whenever a class asks for IProductService, give it an instance of ProductService."
This allows you to change the internal logic of ProductService in the future without ever changing the code in your Controllers or Views.
Step 3: Connecting the Service to the Home Controller
In a professional ASP.NET Core MVC application, the Controller should not talk directly to the database. Instead, it calls the Service Layer. In this step, we use Constructor Injection to bring our IProductService into the HomeController.
The Code: HomeController.cs & HomeViewModel
Update your Controllers/HomeController.cs with the following code:
using Microsoft.AspNetCore.Mvc;
using MobileShop.Models;
using MobileShop.Services;
using System.Diagnostics;
namespace MobileShop.Controllers
{
public class HomeController : Controller
{
private readonly IProductService _productService;
private readonly ILogger<HomeController> _logger;
public HomeController(IProductService productService, ILogger<HomeController> logger)
{
_productService = productService;
_logger = logger;
}
public async Task<IActionResult> Index()
{
var viewModel = new HomeViewModel
{
FeaturedProducts = await _productService.GetFeaturedProductsAsync(8),
NewArrivals = await _productService.GetNewArrivalsAsync(8),
Bestsellers = await _productService.GetBestsellersAsync(8),
Brands = await _productService.GetBrandsAsync(6)
};
return View(viewModel);
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
// ViewModel to hold all homepage data in one object
public class HomeViewModel
{
public List<Product> FeaturedProducts { get; set; } = new List<Product>();
public List<Product> NewArrivals { get; set; } = new List<Product>();
public List<Product> Bestsellers { get; set; } = new List<Product>();
public List<Brand> Brands { get; set; } = new List<Brand>();
}
}
Detailed Technical Explanation
1. The Role of the ViewModel
In MVC Architecture, a View can usually only accept one Model object. However, a Home Page often needs to display several different things (banners, products, brands). By creating the HomeViewModel, we "package" all these lists into one object so the View can access everything easily.
2. Constructor Dependency Injection
Notice the line _productService = productService;. Because we registered our service in Program.cs earlier, ASP.NET Core automatically provides the HomeController with the correct implementation. This is the hallmark of clean, maintainable C# code.
3. High-Performance Data Fetching
Inside the Index() method, we use await for every service call. This ensures that our server can handle other user requests while waiting for the database to return the product lists. This is a critical feature for a fast-loading Mobile Shop Website.
4. SEO & User Experience (UX)
By passing 8 items for products and 6 for brands, we are optimizing the Visual Balance of the home page. Too many items can slow down the page (bad for SEO), while too few look empty. This specific count ensures a perfect grid layout on both mobile and desktop.
Step 4: Creating a Reusable Product Card Partial View
In modern web development, we want our UI to be consistent. Instead of writing the HTML for a product card over and over again, we create a Partial View. This allows us to maintain the design in one single file: _ProductCard.cshtml.
The Code: Views/Shared/_ProductCard.cshtml
Create a new file in your Views/Shared folder named _ProductCard.cshtml and paste the following:
@model Product
<div class="card h-100 product-card shadow-sm">
<div class="position-relative">
<a asp-controller="Products" asp-action="Details" asp-route-id="@Model.Id">
<img src="@Model.MainImageUrl"
class="card-img-top" alt="@Model.Name" style="height: 250px; object-fit: cover;" />
</a>
@if (Model.DiscountPercentage > 0)
{
<span class="badge bg-danger position-absolute top-0 end-0 m-2">-@Model.DiscountPercentage%</span>
}
@if (Model.IsNewArrival)
{
<span class="badge bg-success position-absolute top-0 start-0 m-2">New</span>
}
<button class="btn btn-light btn-sm position-absolute bottom-0 end-0 m-2 wishlist-btn"
data-product-id="@Model.Id">
<i class="bi bi-heart"></i>
</button>
</div>
<div class="card-body d-flex flex-column">
<p class="text-muted mb-1 small">@Model.Brand?.Name</p>
<h6 class="card-title">
<a asp-controller="Products" asp-action="Details" asp-route-id="@Model.Id" class="text-decoration-none text-dark">
@Model.Name
</a>
</h6>
<!-- Star Rating Section -->
<div class="mb-2">
@if (Model.AverageRating > 0)
{
<span class="text-warning">
@for (int i = 1; i <= 5; i++)
{
if (i <= Model.AverageRating)
{
<i class="bi bi-star-fill"></i>
}
else if (i - 0.5 <= Model.AverageRating)
{
<i class="bi bi-star-half"></i>
}
else
{
<i class="bi bi-star"></i>
}
}
</span>
<small class="text-muted">(@Model.ReviewCount)</small>
}
</div>
<div class="mt-auto">
<div class="d-flex align-items-center justify-content-between">
<div>
@if (Model.OriginalPrice > Model.SalePrice)
{
<del class="text-muted small">RS @Model.OriginalPrice.ToString("N0")</del>
}
<h5 class="text-primary mb-0">RS @Model.SalePrice.ToString("N0")</h5>
</div>
<form asp-controller="Cart" asp-action="AddToCart" method="post" class="d-inline">
<input type="hidden" name="productId" value="@Model.Id" />
<button type="submit" class="btn btn-primary btn-sm" @(Model.StockQuantity <= 0 ? "disabled" : "")>
<i class="bi bi-cart-plus"></i> Add
</button>
</form>
</div>
<!-- Stock Alerts -->
@if (Model.StockQuantity <= 5 && Model.StockQuantity > 0)
{
<small class="text-warning"><i class="bi bi-exclamation-circle"></i> Only @Model.StockQuantity left</small>
}
else if (Model.StockQuantity == 0)
{
<small class="text-danger"><i class="bi bi-x-circle"></i> Out of stock</small>
}
</div>
</div>
</div>
Detailed Explanation
1. Partial Views in ASP.NET Core
A Partial View is a Razor file that is rendered inside another view. By naming it with an underscore (_), we follow the convention that this file is a reusable component rather than a standalone page.
2. Dynamic UI with Razor Logic
The code includes several logic checks that make the user interface Dynamic and Reactive:
Discount Badge: Only shows if
DiscountPercentageis greater than zero.Star Rating System: Uses a
@forloop to render star icons dynamically based on theAverageRating.Stock Status: Changes color and text (Warning for low stock, Red for out of stock) to create a sense of urgency for the buyer.
3. Bootstrap 5 Integration
We are using Bootstrap 5 utility classes like shadow-sm, d-flex, and position-absolute to create a modern "Amazon-style" product card. The object-fit: cover style ensures that images look consistent even if they have different original dimensions.
Step 5: Designing the Home Page UI in Index.cshtml
Now it’s time to display our data. We will use Bootstrap 5 Grid and Razor Foreach Loops to loop through our product lists and render them beautifully.
The Code: Views/Home/Index.cshtml
Replace the content of your Views/Home/Index.cshtml with the following code:
@using MobileShop.Controllers
@model HomeViewModel
@{
ViewData["Title"] = "Home";
}
<!-- Featured Products Section -->
<section class="py-5">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Featured Products</h2>
<a class="btn btn-outline-primary">View All <i class="bi bi-arrow-right"></i></a>
</div>
<div class="row g-4">
@foreach (var product in Model.FeaturedProducts)
{
<div class="col-lg-3 col-md-4 col-sm-6">
<partial name="_ProductCard" model="product" />
</div>
}
</div>
</div>
</section>
<!-- New Arrivals Section -->
<section class="py-5 bg-light">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>New Arrivals</h2>
<a class="btn btn-outline-primary">View All <i class="bi bi-arrow-right"></i></a>
</div>
<div class="row g-4">
@foreach (var product in Model.NewArrivals.Take(4))
{
<div class="col-lg-3 col-md-4 col-sm-6">
<partial name="_ProductCard" model="product" />
</div>
}
</div>
</div>
</section>
<!-- Bestsellers Section -->
<section class="py-5">
<div class="container">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Bestsellers</h2>
<a class="btn btn-outline-primary">View All <i class="bi bi-arrow-right"></i></a>
</div>
<div class="row g-4">
@foreach (var product in Model.Bestsellers.Take(4))
{
<div class="col-lg-3 col-md-4 col-sm-6">
<partial name="_ProductCard" model="product" />
</div>
}
</div>
</div>
</section>
<!-- Popular Brands Section -->
<section class="py-5 bg-light">
<div class="container">
<h2 class="text-center mb-5">Popular Brands</h2>
<div class="row g-4 text-center">
@foreach (var brand in Model.Brands)
{
<div class="col-md-2 col-4">
<a asp-controller="Products" asp-action="Index" asp-route-brandId="@brand.Id" class="text-decoration-none">
<div class="brand-card p-4 bg-white rounded shadow-sm">
<h5 class="text-dark mb-0">@brand.Name</h5>
</div>
</a>
</div>
}
</div>
</div>
</section>
Detailed Explanation
1. Mastering the Grid System
We use col-lg-3 col-md-4 col-sm-6. This is critical for Mobile-First Design. It tells the browser:
Show 4 products per row on large screens (Desktops).
Show 3 products per row on medium screens (Tablets).
Show 2 products per row on small screens (Mobiles). This improves your Mobile-Friendly SEO score.
2. Using the Partial Tag Helper
Instead of writing the card HTML four times, we use <partial name="_ProductCard" model="product" />. This makes the code incredibly easy to update. If you want to change the look of your products later, you only edit one file!
3. Performance Optimization with .Take()
Even though our service might fetch 8 items, we use .Take(4) in specific sections like "New Arrivals" to keep the home page looking clean. This ensures the page doesn't become too long, reducing "scroll fatigue" for your users.
4. Semantic HTML5 Tags
Notice the use of <section> tags. Using Semantic HTML helps search engines understand the structure of your page, which is a key factor in ranking for keywords like "Mobile Shop Online" or "Latest Smartphone Deals."
Step 6: Setting Up the Professional _Layout.cshtml
The _Layout.cshtml file is the master template for your entire website. In this step, we configure the global navigation, search bar, and footer to ensure a consistent user experience across the Mobile Shop.
The Code: Views/Shared/_Layout.cshtml
Update your main layout file with the following code. It includes Bootstrap 5, Bootstrap Icons, and a modern e-commerce header.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Mobile Shop</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
@await RenderSectionAsync("Styles", required: false)
</head>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand fw-bold" asp-controller="Home" asp-action="Index">
<i class="bi bi-phone-fill"></i> Mobile Shop
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link">Products</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">Brands</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item">Apple</a></li>
<li><a class="dropdown-item">Samsung</a></li>
<li><a class="dropdown-item">Xiaomi</a></li>
<li><a class="dropdown-item">OnePlus</a></li>
<li><a class="dropdown-item">Google</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item">View All</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link">Contact</a>
</li>
</ul>
<!-- Search Bar -->
<form class="d-flex me-3" method="get">
<div class="input-group">
<input type="search" name="search" class="form-control" placeholder="Search phones..."
id="searchInput" autocomplete="off" />
<button class="btn btn-light" type="submit"><i class="bi bi-search"></i></button>
</div>
</form>
<ul class="navbar-nav">
<!-- Cart -->
<li class="nav-item">
<a class="nav-link position-relative">
<i class="bi bi-cart3 fs-5"></i>
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger" id="cartCount">
0
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link">Login</a>
</li>
<li class="nav-item">
<a class="nav-link">Register</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<!-- Search Suggestions Dropdown -->
<div id="searchSuggestions" class="position-absolute w-100" style="z-index: 1050; display: none;">
<div class="container">
<div class="list-group shadow" id="suggestionsList"></div>
</div>
</div>
<main role="main">
@if (TempData["Success"] != null)
{
<div class="alert alert-success alert-dismissible fade show m-3" role="alert">
<i class="bi bi-check-circle-fill"></i> @TempData["Success"]
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
@if (TempData["Error"] != null)
{
<div class="alert alert-danger alert-dismissible fade show m-3" role="alert">
<i class="bi bi-exclamation-triangle-fill"></i> @TempData["Error"]
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
@RenderBody()
</main>
<footer class="bg-dark text-light mt-5">
<div class="container py-5">
<div class="row">
<div class="col-md-4">
<h5><i class="bi bi-phone-fill"></i> MobileShop</h5>
<p>Your one-stop destination for the latest smartphones at the best prices.</p>
<div class="social-links">
<a href="#" class="text-light me-3"><i class="bi bi-facebook fs-4"></i></a>
<a href="#" class="text-light me-3"><i class="bi bi-twitter-x fs-4"></i></a>
<a href="#" class="text-light me-3"><i class="bi bi-instagram fs-4"></i></a>
<a href="#" class="text-light"><i class="bi bi-youtube fs-4"></i></a>
</div>
</div>
<div class="col-md-2">
<h6>Quick Links</h6>
<ul class="list-unstyled">
<li><a class="text-light text-decoration-none">About Us</a></li>
<li><a class="text-light text-decoration-none">Contact</a></li>
<li><a class="text-light text-decoration-none">Privacy Policy</a></li>
<li><a class="text-light text-decoration-none">Terms of Service</a></li>
</ul>
</div>
<div class="col-md-3">
<h6>Customer Service</h6>
<ul class="list-unstyled">
<li><a class="text-light text-decoration-none">Track Order</a></li>
<li><a class="text-light text-decoration-none">Shipping Info</a></li>
<li><a class="text-light text-decoration-none">Returns & Exchanges</a></li>
<li><a class="text-light text-decoration-none">FAQ</a></li>
</ul>
</div>
<div class="col-md-3">
<h6>Contact Us</h6>
<p><i class="bi bi-geo-alt"></i> 123 Mobile Street, Tech City</p>
<p><i class="bi bi-telephone"></i> +92-3143076781</p>
<p><i class="bi bi-envelope"></i> support@mobileshop.com</p>
<p><i class="bi bi-clock"></i> Mon - Sat: 9AM - 8PM</p>
</div>
</div>
<hr class="my-4">
<div class="text-center">
<p class="mb-0">© @DateTime.Now.Year MobileShop. All rights reserved.</p>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Detailed Technical Explanation
To help your students understand why this layout is professional-grade, let's break down the key features:
1. Modern Navigation & Brand Identity
The navbar uses bg-primary for a bold, professional look. Using the Tag Helper asp-controller="Home" asp-action="Index" ensures that clicking the logo always returns the user to the home page, which is vital for Site Navigation SEO.
2. Integrated Search & Cart UI
Search Bar: We’ve added a search input with an
id="searchInput". This is ready for advanced features like AJAX suggestions, which we will cover in future parts.Cart Badge: The shopping cart uses a
position-absolutebadge to show the item count. This "sticky" notification style is a high-conversion design pattern used by top e-commerce sites.
3. TempData for User Feedback
We’ve included two @if blocks for TempData["Success"] and TempData["Error"].
Why? This allows the server to send "Success" or "Error" messages (like "Item added to cart") that appear as beautiful Bootstrap alerts at the top of the page.
4. The @RenderBody() and Sections
@RenderBody(): This is where our
Index.cshtmlcontent is injected.RenderSectionAsync: We use this for "Scripts" and "Styles". This allows specific pages to load their own custom CSS or JavaScript files only when needed, keeping the rest of the site fast and optimized.
5. Professional Footer & Social SEO
The footer is divided into four columns:
About Us: Briefly describes the shop (great for keyword density).
Quick Links & Customer Service: Improves internal linking for better Google crawling.
Contact Info: Including a real phone number and address increases your site's "Trust Score" with search engines.
Conclusion of Part 4
We have successfully set up the Home Page Settings for our Mobile Shop project! We covered:
Creating a Service Layer for data.
Registering services in Program.cs.
Connecting the Home Controller and ViewModel.
Designing a reusable Product Card partial view.
Finalizing the Home View and Global Layout.
Download Product Image

Comments
Post a Comment