Introduction
Welcome back to the Mobile Shop Website development series. In Part 3, 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."
Conclusion
Congratulations! You have successfully implemented the Home Page logic and UI for your ASP.NET Core MVC Mobile Shop. We’ve covered everything from Service Layers and Dependency Injection to ViewModels and Partial Views.

Comments
Post a Comment