Product Module in ECommerce Application

How to Design and Develop Product Module in ECommerce Application

In this article, I will discuss How to Design and Develop the Product Module for our ECommerce Application using ASP.NET Core Web API and EF Core. Please read our previous article discussing the Category Module in our ECommerce Application.

The Product Module is essential to managing the merchandise available on the E-Commerce platform. It efficiently handles the organization, detailing, and inventory of products, ensuring that each item is accurately represented and readily available for customers. This module supports features like stock management, discount application, and association with product categories, enhancing operational efficiency and customer satisfaction.

Key Features of the Product Module in an ECommerce Application

The following are the Key Features of the Product Module that we will implement in our Ecommerce application:

CRUD Operations
  • Create: Add new products with detailed information, including name, description, price, and category association.
  • Read: Retrieve detailed information about products for display on the platform and for administrative purposes.
  • Update: Modify existing product details to reflect changes in inventory, pricing, or other attributes.
  • Delete: Remove products that are no longer available, ensuring that deletions are handled safely to maintain data integrity.

Stock Management: Monitor and manage product stock levels to prevent overselling and ensure inventory accuracy. Automatically update stock quantities based on order placements and cancellations.

Discount Management: Implement percentage-based product discounts to facilitate sales promotions and marketing strategies.

Image Handling: Manage and validate product image URLs to ensure visual representations are correctly displayed on the platform.

Association with Categories: Ensure each product is correctly associated with its respective category, enhancing product discoverability and organization. Maintain referential integrity between products and categories to support navigation and management.

Data Transfer Objects (DTOs)

DTOs (Data Transfer Objects) are pivotal in structuring and validating the data exchanged between the client and server. They ensure that only the necessary and validated data is processed, enhancing security and maintaining a clear separation between internal data models and external API contracts. First, create a folder named ProductDTOs within the DTOs folder where we will create the Required Request and Response DTOs for managing the Products.

ProductCreateDTO

Create a class file named ProductCreateDTO.cs within the DTOs/ProductDTOs folder, and then copy and paste the following code. The ProductCreateDTO Facilitates the creation of new products by encapsulating all necessary product-related data. It is used when administrators add new products to the inventory, ensuring that all required fields are provided and validated before processing.

using System.ComponentModel.DataAnnotations;
namespace ECommerceApp.DTOs.ProductDTOs
{
    // DTO for creating a new product
    public class ProductCreateDTO
    {
        [Required(ErrorMessage = "Product Name is required.")]
        [StringLength(100, MinimumLength = 3, ErrorMessage = "Product Name must be between 3 and 100 characters.")]
        public string Name { get; set; }

        [Required(ErrorMessage = "Description is required.")]
        [MinLength(10, ErrorMessage = "Description must be at least 10 characters.")]
        public string Description { get; set; }

        [Range(0.01, 10000.00, ErrorMessage = "Price must be between $0.01 and $10,000.00.")]
        public decimal Price { get; set; }

        [Range(0, 1000, ErrorMessage = "Stock Quantity must be between 0 and 1000.")]
        public int StockQuantity { get; set; }

        [Url(ErrorMessage = "Invalid Image URL.")]
        public string ImageUrl { get; set; }

        [Range(0, 100, ErrorMessage = "Discount Percentage must be between 0% and 100%.")]
        public int DiscountPercentage { get; set; }

        [Required(ErrorMessage = "Category ID is required.")]
        public int CategoryId { get; set; }
    }
}
ProductResponseDTO

Create a class file named ProductResponseDTO.cs within the DTOs/ProductDTOs folder, and then copy and paste the following code. The ProductResponseDTO Represents the response structure for product-related data, providing comprehensive product details and associated category information. It is used in API responses to deliver product details to clients, ensuring that necessary information is included for display and further processing.

namespace ECommerceApp.DTOs.ProductDTOs
{
    // DTO for returning product details.
    public class ProductResponseDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public int StockQuantity { get; set; }
        public string ImageUrl { get; set; }
        public int DiscountPercentage { get; set; }
        public int CategoryId { get; set; }
        public bool IsAvailable { get; set; }
    }
}
ProductUpdateDTO

Create a class file named ProductUpdateDTO.cs within the DTOs/ProductDTOs folder, then copy and paste the following code. The ProductUpdateDTO Facilitates updating existing products by encapsulating all necessary product-related data, including the Product ID, which needs to be updated. It is used when administrators update existing products, ensuring all required fields are provided and validated before processing.

using System.ComponentModel.DataAnnotations;
namespace ECommerceApp.DTOs.ProductDTOs
{
    public class ProductUpdateDTO
    {
        [Required(ErrorMessage = "Product Id is required.")]
        public int Id { get; set; }

        [Required(ErrorMessage = "Product Name is required.")]
        [StringLength(100, MinimumLength = 3, ErrorMessage = "Product Name must be between 3 and 100 characters.")]
        public string Name { get; set; }

        [Required(ErrorMessage = "Description is required.")]
        [MinLength(10, ErrorMessage = "Description must be at least 10 characters.")]
        public string Description { get; set; }

        [Range(0.01, 10000.00, ErrorMessage = "Price must be between $0.01 and $10,000.00.")]
        public decimal Price { get; set; }

        [Range(0, 1000, ErrorMessage = "Stock Quantity must be between 0 and 1000.")]
        public int StockQuantity { get; set; }

        [Url(ErrorMessage = "Invalid Image URL.")]
        public string ImageUrl { get; set; }

        [Range(0, 100, ErrorMessage = "Discount Percentage must be between 0% and 100%.")]
        public int DiscountPercentage { get; set; }

        [Required(ErrorMessage = "Category ID is required.")]
        public int CategoryId { get; set; }
    }
}
ProductStatusUpdateDTO:

Create a class file named ProductStatusUpdateDTO.cs within the DTOs/ProductDTOs folder and then copy and paste the following code. The ProductStatusUpdateDTO is used to make an existing product active and inactive.

using System.ComponentModel.DataAnnotations;
namespace ECommerceApp.DTOs.ProductDTOs
{
    public class ProductStatusUpdateDTO
    {
        [Required(ErrorMessage = "ProductId is required.")]
        public int ProductId {  get; set; }

        [Required(ErrorMessage = "IsAvailable is required.")]
        public bool IsAvailable { get; set; }
    }
}

Product Service

To enhance the maintainability, scalability, and separation of concerns within our Ecommerce Application, it’s essential to segregate business logic and data access operations from the controller. This can be effectively achieved by introducing a Service Class dedicated to managing Products.

The service class encapsulates all business logic and data access operations, ensuring the controller remains clean and focused solely on handling HTTP requests and responses. Create a new class file named ProductService.cs within the Services folder and add the following code:

using Microsoft.EntityFrameworkCore;
using ECommerceApp.Data;
using ECommerceApp.DTOs.ProductDTOs;
using ECommerceApp.Models;
using ECommerceApp.DTOs;

namespace ECommerceApp.Services
{
    public class ProductService
    {
        private readonly ApplicationDbContext _context;

        public ProductService(ApplicationDbContext context)
        {
            _context = context;
        }

        public async Task<ApiResponse<ProductResponseDTO>> CreateProductAsync(ProductCreateDTO productDto)
        {
            try
            {
                // Check if product name already exists (case-insensitive)
                if (await _context.Products.AnyAsync(p => p.Name.ToLower() == productDto.Name.ToLower()))
                {
                    return new ApiResponse<ProductResponseDTO>(400, "Product name already exists.");
                }

                // Check if Category exists
                if (!await _context.Categories.AnyAsync(cat => cat.Id == productDto.CategoryId))
                {
                    return new ApiResponse<ProductResponseDTO>(400, "Specified category does not exist.");
                }

                // Manual mapping from DTO to Model
                var product = new Product
                {
                    Name = productDto.Name,
                    Description = productDto.Description,
                    Price = productDto.Price,
                    StockQuantity = productDto.StockQuantity,
                    ImageUrl = productDto.ImageUrl,
                    DiscountPercentage = productDto.DiscountPercentage,
                    CategoryId = productDto.CategoryId,
                    IsAvailable = true
                };

                // Add product to the database
                _context.Products.Add(product);
                await _context.SaveChangesAsync();

                // Map to ProductResponseDTO
                var productResponse = new ProductResponseDTO
                {
                    Id = product.Id,
                    Name = product.Name,
                    Description = product.Description,
                    Price = product.Price,
                    StockQuantity = product.StockQuantity,
                    ImageUrl = product.ImageUrl,
                    DiscountPercentage = product.DiscountPercentage,
                    CategoryId = product.CategoryId,
                    IsAvailable = product.IsAvailable
                };

                return new ApiResponse<ProductResponseDTO>(200, productResponse);
            }
            catch (Exception ex)
            {
                // Log the exception (implementation depends on your logging setup)
                return new ApiResponse<ProductResponseDTO>(500, $"An unexpected error occurred while processing your request, Error: {ex.Message}");
            }
        }

        public async Task<ApiResponse<ProductResponseDTO>> GetProductByIdAsync(int id)
        {
            try
            {
                var product = await _context.Products
                    .AsNoTracking()
                    .FirstOrDefaultAsync(p => p.Id == id);

                if (product == null)
                {
                    return new ApiResponse<ProductResponseDTO>(404, "Product not found.");
                }

                // Map to ProductResponseDTO
                var productResponse = new ProductResponseDTO
                {
                    Id = product.Id,
                    Name = product.Name,
                    Description = product.Description,
                    Price = product.Price,
                    StockQuantity = product.StockQuantity,
                    ImageUrl = product.ImageUrl,
                    DiscountPercentage = product.DiscountPercentage,
                    CategoryId = product.CategoryId,
                    IsAvailable = product.IsAvailable
                };

                return new ApiResponse<ProductResponseDTO>(200, productResponse);
            }
            catch (Exception ex)
            {
                // Log the exception
                return new ApiResponse<ProductResponseDTO>(500, $"An unexpected error occurred while processing your request, Error: {ex.Message}");
            }
        }

        public async Task<ApiResponse<ConfirmationResponseDTO>> UpdateProductAsync(ProductUpdateDTO productDto)
        {
            try
            {
                var product = await _context.Products.FindAsync(productDto.Id);
                if (product == null)
                {
                    return new ApiResponse<ConfirmationResponseDTO>(404, "Product not found.");
                }

                // Check if the new product name already exists (case-insensitive), excluding the current product
                if (await _context.Products.AnyAsync(p => p.Name.ToLower() == productDto.Name.ToLower() && p.Id != productDto.Id))
                {
                    return new ApiResponse<ConfirmationResponseDTO>(400, "Another product with the same name already exists.");
                }

                // Check if Category exists
                if (!await _context.Categories.AnyAsync(cat => cat.Id == productDto.CategoryId))
                {
                    return new ApiResponse<ConfirmationResponseDTO>(400, "Specified category does not exist.");
                }

                // Update product properties manually
                product.Name = productDto.Name;
                product.Description = productDto.Description;
                product.Price = productDto.Price;
                product.StockQuantity = productDto.StockQuantity;
                product.ImageUrl = productDto.ImageUrl;
                product.DiscountPercentage = productDto.DiscountPercentage;
                product.CategoryId = productDto.CategoryId;

                await _context.SaveChangesAsync();

                // Prepare confirmation message
                var confirmationMessage = new ConfirmationResponseDTO
                {
                    Message = $"Product with Id {productDto.Id} updated successfully."
                };

                return new ApiResponse<ConfirmationResponseDTO>(200, confirmationMessage);
            }
            catch (Exception ex)
            {
                // Log the exception
                return new ApiResponse<ConfirmationResponseDTO>(500, $"An unexpected error occurred while processing your request, Error: {ex.Message}");
            }
        }

        public async Task<ApiResponse<ConfirmationResponseDTO>> DeleteProductAsync(int id)
        {
            try
            {
                var product = await _context.Products
                    .FirstOrDefaultAsync(p => p.Id == id);

                if (product == null)
                {
                    return new ApiResponse<ConfirmationResponseDTO>(404, "Product not found.");
                }

                // Implementing Soft Delete
                product.IsAvailable = false;
                await _context.SaveChangesAsync();

                // Prepare confirmation message
                var confirmationMessage = new ConfirmationResponseDTO
                {
                    Message = $"Product with Id {id} deleted successfully."
                };

                return new ApiResponse<ConfirmationResponseDTO>(200, confirmationMessage);
            }
            catch (Exception ex)
            {
                // Log the exception
                return new ApiResponse<ConfirmationResponseDTO>(500, $"An unexpected error occurred while processing your request, Error: {ex.Message}");
            }
        }

        public async Task<ApiResponse<List<ProductResponseDTO>>> GetAllProductsAsync()
        {
            try
            {
                var products = await _context.Products
                    .AsNoTracking()
                    .ToListAsync();

                var productList = products.Select(p => new ProductResponseDTO
                {
                    Id = p.Id,
                    Name = p.Name,
                    Description = p.Description,
                    Price = p.Price,
                    StockQuantity = p.StockQuantity,
                    ImageUrl = p.ImageUrl,
                    DiscountPercentage = p.DiscountPercentage,
                    CategoryId = p.CategoryId,
                    IsAvailable = p.IsAvailable
                }).ToList();

                return new ApiResponse<List<ProductResponseDTO>>(200, productList);
            }
            catch (Exception ex)
            {
                // Log the exception
                return new ApiResponse<List<ProductResponseDTO>>(500, $"An unexpected error occurred while processing your request, Error: {ex.Message}");
            }
        }

        public async Task<ApiResponse<List<ProductResponseDTO>>> GetAllProductsByCategoryAsync(int categoryId)
        {
            try
            {
                // Retrieve products associated with the specified category
                var products = await _context.Products
                    .AsNoTracking()
                    .Include(p => p.Category)
                    .Where(p => p.CategoryId == categoryId && p.IsAvailable)
                    .ToListAsync();

                if (products == null || products.Count == 0)
                {
                    return new ApiResponse<List<ProductResponseDTO>>(404, "Products not found.");
                }

                var productList = products.Select(p => new ProductResponseDTO
                {
                    Id = p.Id,
                    Name = p.Name,
                    Description = p.Description,
                    Price = p.Price,
                    StockQuantity = p.StockQuantity,
                    ImageUrl = p.ImageUrl,
                    DiscountPercentage = p.DiscountPercentage,
                    CategoryId = p.CategoryId,
                    IsAvailable = p.IsAvailable
                }).ToList();

                return new ApiResponse<List<ProductResponseDTO>>(200, productList);
            }
            catch (Exception ex)
            {
                // Log the exception
                return new ApiResponse<List<ProductResponseDTO>>(500, $"An unexpected error occurred while processing your request, Error: {ex.Message}");
            }
        }

        public async Task<ApiResponse<ConfirmationResponseDTO>> UpdateProductStatusAsync(ProductStatusUpdateDTO productStatusUpdateDTO)
        {
            try
            {
                var product = await _context.Products
                    .FirstOrDefaultAsync(p => p.Id == productStatusUpdateDTO.ProductId);

                if (product == null)
                {
                    return new ApiResponse<ConfirmationResponseDTO>(404, "Product not found.");
                }

                product.IsAvailable = productStatusUpdateDTO.IsAvailable;
                await _context.SaveChangesAsync();

                // Prepare confirmation message
                var confirmationMessage = new ConfirmationResponseDTO
                {
                    Message = $"Product with Id {productStatusUpdateDTO.ProductId} Status Updated successfully."
                };

                return new ApiResponse<ConfirmationResponseDTO>(200, confirmationMessage);
            }
            catch (Exception ex)
            {
                // Log the exception
                return new ApiResponse<ConfirmationResponseDTO>(500, $"An unexpected error occurred while processing your request, Error: {ex.Message}");
            }
        }
    }
}
Registering Services in Dependency Injection Container

To enable dependency injection of the service class, we need to register the service within the application’s dependency injection container. This is typically done in the Program.cs file. So, please modify the Program.cs class file as follows:

using ECommerceApp.Data;
using ECommerceApp.Services;
using Microsoft.EntityFrameworkCore;

namespace ECommerceApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.

            builder.Services.AddControllers()
            .AddJsonOptions(options =>
            {
                // This will use the property names as defined in the C# model
                options.JsonSerializerOptions.PropertyNamingPolicy = null;
            });

            // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
            builder.Services.AddEndpointsApiExplorer();
            builder.Services.AddSwaggerGen();

            // Configure EF Core with SQL Server
            builder.Services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(builder.Configuration.GetConnectionString("EFCoreDBConnection")));

            // Registering the CustomerService
            builder.Services.AddScoped<CustomerService>();

            // Registering the AddressService
            builder.Services.AddScoped<AddressService>();

            // Registering the CategoryService
            builder.Services.AddScoped<CategoryService>();

            // Registering the ProductService
            builder.Services.AddScoped<ProductService>();

            var app = builder.Build();

            // Configure the HTTP request pipeline.
            if (app.Environment.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI();
            }

            app.UseHttpsRedirection();

            app.UseAuthorization();

            app.MapControllers();

            app.Run();
        }
    }
}

Creating Products Controller

The ProductsController now utilizes the ProductService to handle product-related operations. This refactoring results in a cleaner controller that delegates business logic to the service, promoting better code organization and maintainability. Create a new API Empty Controller named ProductsController within the Controllers folder and then copy and paste the following code.

using Microsoft.AspNetCore.Mvc;
using ECommerceApp.DTOs;
using ECommerceApp.DTOs.ProductDTOs;
using ECommerceApp.Services;

namespace ECommerceApp.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        private readonly ProductService _productService;

        // Injecting the ProductService
        public ProductsController(ProductService productService)
        {
            _productService = productService;
        }

        // Creates a new product.
        [HttpPost("CreateProduct")]
        public async Task<ActionResult<ApiResponse<ProductResponseDTO>>> CreateProduct([FromBody] ProductCreateDTO productDto)
        {
            var response = await _productService.CreateProductAsync(productDto);
            if (response.StatusCode != 200)
            {
                return StatusCode(response.StatusCode, response);
            }
            return Ok(response);
        }

        // Retrieves a product by ID.
        [HttpGet("GetProductById/{id}")]
        public async Task<ActionResult<ApiResponse<ProductResponseDTO>>> GetProductById(int id)
        {
            var response = await _productService.GetProductByIdAsync(id);
            if (response.StatusCode != 200)
            {
                return StatusCode(response.StatusCode, response);
            }
            return Ok(response);
        }

        // Updates an existing product.
        [HttpPut("UpdateProduct")]
        public async Task<ActionResult<ApiResponse<ConfirmationResponseDTO>>> UpdateProduct([FromBody] ProductUpdateDTO productDto)
        {
            var response = await _productService.UpdateProductAsync(productDto);
            if (response.StatusCode != 200)
            {
                return StatusCode(response.StatusCode, response);
            }
            return Ok(response);
        }

        // Deletes a product by ID.
        [HttpDelete("DeleteProduct/{id}")]
        public async Task<ActionResult<ApiResponse<ConfirmationResponseDTO>>> DeleteProduct(int id)
        {
            var response = await _productService.DeleteProductAsync(id);
            if (response.StatusCode != 200)
            {
                return StatusCode(response.StatusCode, response);
            }
            return Ok(response);
        }

        // Retrieves all products.
        [HttpGet("GetAllProducts")]
        public async Task<ActionResult<ApiResponse<List<ProductResponseDTO>>>> GetAllProducts()
        {
            var response = await _productService.GetAllProductsAsync();
            if (response.StatusCode != 200)
            {
                return StatusCode(response.StatusCode, response);
            }
            return Ok(response);
        }

        // Retrieves all products by category.
        [HttpGet("GetAllProductsByCategory/{categoryId}")]
        public async Task<ActionResult<ApiResponse<List<ProductResponseDTO>>>> GetAllProductsByCategory(int categoryId)
        {
            var response = await _productService.GetAllProductsByCategoryAsync(categoryId);
            if (response.StatusCode != 200)
            {
                return StatusCode(response.StatusCode, response);
            }
            return Ok(response);
        }

        // Update Product Status
        [HttpPut("UpdateProductStatus")]
        public async Task<ActionResult<ApiResponse<ConfirmationResponseDTO>>> UpdateProductStatus(ProductStatusUpdateDTO productStatusUpdateDTO)
        {
            var response = await _productService.UpdateProductStatusAsync(productStatusUpdateDTO);
            if (response.StatusCode != 200)
            {
                return StatusCode(response.StatusCode, response);
            }
            return Ok(response);
        }
    }
}

Testing the Product Module Endpoints of Our ECommerce Application:

Create Product:

Allows administrators to add new products to the inventory. It is invoked when an administrator submits a request to create a new product, providing necessary details like name, description, price, stock quantity, image URL, discount percentage, and category association. This endpoint allows administrators to add new products to the inventory.

Endpoint: POST /api/Products/CreateProduct
Method: POST
URL: http://localhost:5000/api/Products/CreateProduct
Headers: Content-Type: application/json
Request Body:

{
    "Name": "Smartphone XYZ",
    "Description": "A high-end smartphone with exceptional features.",
    "Price": 799.99,
    "StockQuantity": 50,
    "ImageUrl": "https://example.com/images/smartphone_xyz.jpg",
    "DiscountPercentage": 10,
    "CategoryId": 1
}

Response in Swagger

How to Design and Develop the Product Module for our ECommerce Application using ASP.NET Core Web API and EF Core

Get Product by ID:

Retrieves detailed information about a specific product based on its unique identifier. It is used when administrators or clients need to view details of a particular product, such as its name, description, price, stock quantity, image URL, discount percentage, and associated category. This endpoint Retrieves detailed information about a specific product.

Endpoint: GET /api/Products/GetProductById/{id}
Method: GET
URL: http://localhost:5000/api/Products/GetProductById/4
Response in Swagger

How to Design and Develop the Product Module for our ECommerce Application using ASP.NET Core Web API

Update Product:

Enables administrators to modify existing product information. It is invoked when an administrator updates a product’s details, such as name, description, price, stock quantity, image URL, discount percentage, or category association. This endpoint Enables administrators to modify existing product information.

Endpoint: PUT /api/Products/UpdateProduct
Method: PUT
URL: http://localhost:5000/api/Products/UpdateProduct
Headers: Content-Type: application/json
Request Body:

{
    "Id": 4,
    "Name": "Smartphone XYZ Pro",
    "Description": "An upgraded version of the high-end smartphone with additional features.",
    "Price": 899.99,
    "StockQuantity": 40,
    "ImageUrl": "https://example.com/images/smartphone_xyz_pro.jpg",
    "DiscountPercentage": 15,
    "CategoryId": 1
}

Response in Swagger

How to Design and Develop the Product Module for our ECommerce Application

Delete Product:

Allows administrators to remove a product from the inventory. It is used when a product is no longer available for sale or needs to be retired from the catalog, provided it isn’t associated with any existing order items. This endpoint allows administrators to remove a product from the inventory.

Endpoint: DELETE /api/Products/DeleteProduct/{id}
Method: DELETE
URL: http://localhost:5000/api/Products/DeleteProduct/4

Note: We are not going to Delete the Product.

Get All Products:

Retrieves a comprehensive list of all products in the system. It is used when administrators or clients need to view all available products. This endpoint retrieves a comprehensive list of all products in the system.

Endpoint: GET /api/Products/GetAllProducts
Method: GET
URL: http://localhost:5000/api/Products/GetAllProducts
Response in Swagger: It should return all the Products:

Get All Products by Category:

Retrieves a comprehensive list of all products based on a specific category. It is used when administrators or clients need to view all available products for a specific category. This endpoint Retrieves all products associated with a particular category.

Endpoint: GET /api/Products/GetAllProductsByCategory/{categoryId}
Method: GET
URL: http://localhost:5000/api/Products/GetAllProductsByCategory/2
Response in Swagger:

Product Module for our ECommerce Application

Update Product Status:

Enables administrators to update an existing product status. It is invoked when an administrator wants to make the product active or inactive.
Endpoint: PUT /api/Products/UpdateProductStatus
Method: PUT
URL: http://localhost:5000/api/Products/UpdateProductStatus
Headers: Content-Type: application/json
Request Body:

{
  "ProductId": 1,
  "IsAvailable": true
}

Response in Swagger:

Update Product Status

So, we have completed the Product Module Implementation in our Ecommerce Application using ASP.NET Core Web API and Entity Framework Core. In the next article, I will discuss How to Implement the Shopping Cart Module of our ECommerce Application for Product Inventory Management. In this article, we discussed How to Design and Develop the Product Module for our ECommerce Application using ASP.NET Core Web API and EF Core, and I hope you enjoy the article.

Leave a Reply

Your email address will not be published. Required fields are marked *