Back to: ASP.NET Core Web API Tutorials
How to Use Automapper in ASP.NET Core Web API
In this article, I will discuss how to use Automapper in the ASP.NET Core Web API Application with examples. Please read our previous article discussing Model Binding in ASP.NET Core Web API with Examples. First, I will discuss the concepts of Models/Entities and DTOs, explain the necessity of Data Transfer Objects (DTOs), and then provide a real-time example of integrating AutoMapper with EF Core in an ASP.NET Core Web API application.
Automapper in ASP.NET Core Web API
When developing an ASP.NET Core Web API application, it’s common to have different layers where each layer has its own representation of data. For example:
- The Data Layer (often using Entity Framework Core) may have entity classes (Models) that match the database schema.
- The Application Layer (or API Layer) may present DTOs (Data Transfer Objects) specific to the client’s needs.
Manually converting (mapping) the data between Models and DTOs can be tedious and error-prone, especially as the application grows and the number of fields increases.
AutoMapper is a powerful library in ASP.NET Core that simplifies the task of mapping one object to another. This is useful in scenarios where we have complex data models and need to map between similar types but not identical ones. AutoMapper reduces the amount of manual mapping code, making the codebase cleaner, more maintainable, and less error-prone.
What are Models (Entities)?
In an ASP.NET Core Web API application, models (or entities) represent the data structure in the database and map directly to database tables. They contain every field needed to store data, including internal or sensitive properties (e.g., SupplierCost, SupplierInfo, StockQuantity, SKU, Brand, or timestamps for a Product entity) that are important for business logic but should not be exposed to external consumers.
What are DTOs?
DTOs (Data Transfer Objects) are simplified versions of models designed for data exchange between layers, especially between the server and the client. They contain only the necessary fields that should be exposed to the client. By omitting sensitive or unnecessary details, DTOs help ensure:
- Security: DTOs ensure that only relevant data is transferred to the client, preventing exposure of sensitive information like passwords, salary, or any sensitive information.
- Performance: Transferring only necessary data improves performance by reducing payload size.
- Separation of Concerns: By using DTOs, the internal structure of our database remains decoupled from our API contracts.
- Data Shaping: We can modify or rename fields (e.g., change Name to ProductName) to match client expectations without altering our core domain model.
For example, in an E-commerce system, a Product entity may contain sensitive data such as SupplierCost, SupplierInfo, and StockQuantity, which should not be exposed to customers. We can create a ProductDTO that includes only customer-facing properties such as Name, Price, and Category.
Example to Understand Automapper in ASP.NET Core Web API:
Let’s understand AutoMapper in ASP.NET Core Web API with a real-time example of an E-commerce system that manages products. The Product entity in our database might contain details, including sensitive information like supplier cost and supplier info, which should not be exposed to customers. Instead, we will expose only customer-friendly data through an API. We will first implement the example without using AutoMapper, discuss potential drawbacks, and then solve the same problem using AutoMapper.
Setting Up the Project and Installing Entity Framework Core
First, create a new ASP.NET Core Web API Application named AutomapperDemo. Once you create the project, please install the Entity Framework Core Packages by executing the following command in the Package Manager Console. I am using SQL Server as the backend database, and hence, I am using SQL Server data provider for EF Core.
- Install-Package Microsoft.EntityFrameworkCore.SqlServer
- Install-Package Microsoft.EntityFrameworkCore.Tools
Define Models and DTOs:
We will create the Product entity representing the database model and a ProductDTO for client interaction. First, create two folders named Models and DTOs in the project root directory. Inside the Models folder, we will create our Models or Entities, and inside the DTOs, we will create all our DTOs,
Product Model:
Create a class file named Product.cs within the Models folder and copy and paste the following code. This entity represents the database record and contains customer-facing and internal/sensitive fields. The Product entity contains fields like SupplierCost, SupplierInfo, and StockQuantity that we don’t want to expose to customers. These fields are necessary for internal business logic but irrelevant to the client.
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace AutomapperDemo.Models { public class Product { public int Id { get; set; } public string? SKU { get; set; } // Unique Product Identifier [Required] public string Name { get; set; } public string? Description { get; set; } [Range(0.01, 1000000)] [Column(TypeName = "decimal(18,2)")] public decimal Price { get; set; } public bool IsAvailable { get; set; } public string Category { get; set; } public string Brand { get; set; } public DateTime CreatedDate { get; set; } public DateTime? UpdatedDate { get; set; } // Sensitive/Internal Fields (not to be exposed to customers) [Column(TypeName = "decimal(18,2)")] public decimal SupplierCost { get; set; } public string SupplierInfo { get; set; } [Range(0, int.MaxValue)] public int StockQuantity { get; set; } } }
What Is a Product SKU?
A product SKU (Stock Keeping Unit) is a unique alphanumeric identifier assigned to each product that helps businesses track inventory, manage stock levels, and process sales. The SKU uniquely identifies a product in a retailer’s or manufacturer’s inventory system. It distinguishes one product from another.
SKUs often incorporate meaningful codes such as product category, size, color, brand, or model that help quickly identify key attributes of the product. For example, an SKU like ELE-TEC-GAM-2025-1 might indicate an Electronics Gaming Laptop with TechBrand introduced in 2025. In our example, each SKU is constructed as follows:
- The first three letters of the Category.
- The first three letters of the Brand.
- First three letters of the Product Name.
- Year of the Product (from its CreatedDate).
- The Integer ID of the Product.
For example, for a product with the following details:
- Category: Electronics → ELE
- Brand: TechBrand → TEC
- Product Name: Gaming Laptop → GAM
- CreatedDate: January 1, 2025 (year 2025)
- Id: 1
The SKU becomes: ELE-TEC-GAM-2025-1
Defining DTOs:
In real-time applications, the best way to implement DTOs is to create separate DTOs for different operations, such as retrieving, adding, or updating an entity. This approach allows us to control what data is included based on the operation. For example, when adding a product, we might need to send more data (like internal details such as SupplierInfo and StockQuantity), but when retrieving a product, we might only want to expose customer-friendly information.
Customer-Facing DTO (ProductDTO)
Create a class file named ProductDTO.cs within the DTOs folder, and copy and paste the following code. In this DTO, we purposely rename some properties (Name becomes ProductName and Description becomes ShortDescription) to illustrate a scenario where property names differ between source and destination. When fetching product details for display to customers, sensitive or internal information like SupplierInfo, StockQuantity, or SupplierCost should not be exposed.
namespace AutomapperDemo.DTOs { public class ProductDTO { public int Id { get; set; } public string? SKU { get; set; } // Renamed properties to simulate different naming conventions public string ProductName { get; set; } public string? ShortDescription { get; set; } public decimal Price { get; set; } public bool IsAvailable { get; set; } public string Category { get; set; } public string Brand { get; set; } public DateTime CreatedDate { get; set; } // Sensitive fields like SupplierCost, SupplierInfo, and StockQuantity are intentionally omitted. } }
Admin/Internal DTO (ProductCreateDTO)
Create a class file named ProductCreateDTO.cs within the DTOs folder, and then copy and paste the following code. This DTO is used when creating a new product and includes all fields (including sensitive ones) that an admin may provide.
using System.ComponentModel.DataAnnotations; namespace AutomapperDemo.DTOs { public class ProductCreateDTO { [Required] public string Name { get; set; } public string? Description { get; set; } [Range(0.01, 1000000)] public decimal Price { get; set; } public string Category { get; set; } public string Brand { get; set; } // Sensitive/internal fields public decimal SupplierCost { get; set; } public string SupplierInfo { get; set; } [Range(0, int.MaxValue)] public int StockQuantity { get; set; } } }
Setting Up the Database Context
First, create a folder called Data at the project root directory, and then inside the Data folder, create a class file named ProductDbContext.cs and copy and paste the following code. This context includes some initial seed data for testing purposes.
using AutomapperDemo.Models; using Microsoft.EntityFrameworkCore; namespace AutomapperDemo.Data { public class ProductDbContext : DbContext { public ProductDbContext(DbContextOptions<ProductDbContext> options) : base(options) { } public DbSet<Product> Products { get; set; } // Seed some sample data protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Product>().HasData( new Product { Id = 1, SKU = "ELE-TEC-GAM-2025-1", // Electronics, TechBrand, Gaming Laptop, 2025, Id=1 Name = "Gaming Laptop", Description = "High performance gaming laptop", Price = 1500.99m, IsAvailable = true, Category = "Electronics", Brand = "TechBrand", CreatedDate = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc), SupplierCost = 1200.00m, SupplierInfo = "Tech Supplier Co.", StockQuantity = 50 }, new Product { Id = 2, SKU = "ELE-PHO-SMA-2025-2", // Electronics, PhoneMaker, Smartphone, 2025, Id=2 Name = "Smartphone", Description = "Latest smartphone with cutting edge features", Price = 999.99m, IsAvailable = true, Category = "Electronics", Brand = "PhoneMaker", CreatedDate = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc), SupplierCost = 750.00m, SupplierInfo = "Mobile Solutions Inc.", StockQuantity = 100 } ); } } }
Configure the Database Connection
Next, please update the appsettings.json with the database connection string as follows:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "ECommerceDBConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=ProductsDB;Trusted_Connection=True;TrustServerCertificate=True;" } }
Configure Database Connection and Dependency Injection:
Please modify the Program.cs class file as follows to register our ProductDBContext and other services:
using AutomapperDemo.Data; using Microsoft.EntityFrameworkCore; namespace AutomapperDemo { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers() // Optionally, configure JSON options or other formatter settings .AddJsonOptions(options => { // Configure JSON serializer settings to keep the Original names in serialization and deserialization options.JsonSerializerOptions.PropertyNamingPolicy = null; }); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // Register DbContext builder.Services.AddDbContext<ProductDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("ECommerceDBConnection"))); 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 and Applying Database Migration:
In Visual Studio, Open the Package Manager Console and Execute the Add-Migration and Update-Database commands as follows to generate the Migration file and then apply the Migration file to create the ProductsDB database and required Products table:
Once you execute the above commands and verify the database, you should see the ProductsDB database with the required Products table, as shown in the image below.
Create the Products API Controller Without Using AutoMapper
Let’s first create the ProductsController without using AutoMapper. This controller will manually map data between the Product entity and ProductDTO/ProductCreateDTO. So, create an API Empty Controller named ProductsController within the Controllers folder and copy and paste the following code.
using AutomapperDemo.Data; using AutomapperDemo.DTOs; using AutomapperDemo.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace AutomapperDemo.Controllers { [Route("api/[controller]")] [ApiController] public class ProductsController : ControllerBase { private readonly ProductDbContext _context; public ProductsController(ProductDbContext context) { _context = context; } // GET: api/Products/GetProducts [HttpGet("GetProducts")] public async Task<ActionResult<IEnumerable<ProductDTO>>> GetProducts() { var productDTOs = await _context.Products.AsNoTracking() .Select(product => new ProductDTO{ Id = product.Id, SKU = product.SKU, ProductName = product.Name, // Mapping Name to ProductName ShortDescription = product.Description, // Mapping Description to ShortDescription Price = product.Price, IsAvailable = product.IsAvailable, Category = product.Category, Brand = product.Brand, CreatedDate = product.CreatedDate }) .ToListAsync(); return Ok(productDTOs); } // GET: api/Products/GetProductbyId/{id} [HttpGet("GetProductbyId/{id}")] public async Task<ActionResult<ProductDTO>> GetProductbyId(int id) { var productDTO = await _context.Products .AsNoTracking() .Select(product => new ProductDTO { Id = product.Id, SKU = product.SKU, ProductName = product.Name, // Mapping Name to ProductName ShortDescription = product.Description, // Mapping Description to ShortDescription Price = product.Price, IsAvailable = product.IsAvailable, Category = product.Category, Brand = product.Brand, CreatedDate = product.CreatedDate }) .FirstOrDefaultAsync(prd => prd.Id == id); if (productDTO == null) return NotFound(); return Ok(productDTO); } // POST: api/Products/AddProduct [HttpPost("AddProduct")] public async Task<ActionResult<ProductDTO>> AddProduct(ProductCreateDTO productCreateDTO) { // Manually map ProductCreateDTO to Product entity var product = new Product { Name = productCreateDTO.Name, Description = productCreateDTO.Description, Price = productCreateDTO.Price, Category = productCreateDTO.Category, Brand = productCreateDTO.Brand, SupplierCost = productCreateDTO.SupplierCost, SupplierInfo = productCreateDTO.SupplierInfo, StockQuantity = productCreateDTO.StockQuantity, IsAvailable = productCreateDTO.StockQuantity > 0, CreatedDate = DateTime.UtcNow }; // First, add the product without SKU to generate the Product.Id _context.Products.Add(product); await _context.SaveChangesAsync(); // Now that the product.Id is generated, create the SKU using the new logic: product.SKU = GenerateSKU(product); // Update the product record with the new SKU await _context.SaveChangesAsync(); var productDTO = new ProductDTO { Id = product.Id, SKU = product.SKU, ProductName = product.Name, // Mapping Name to ProductName ShortDescription = product.Description, // Mapping Description to ShortDescription Price = product.Price, IsAvailable = product.IsAvailable, Category = product.Category, Brand = product.Brand, CreatedDate = product.CreatedDate }; return Ok(productDTO); } // Generates a SKU based on: // - First three letters of Category // - First three letters of Brand // - First three letters of Product Name // - Year of CreatedDate // - Product.Id // Example: If Category="Electronics", Brand="Samsung", Name="Galaxy", CreatedDate is 2025 and Id is 15, // The SKU would be "ELE-SAM-GAL-2025-15". private string GenerateSKU(Product product) { // Use default values if any fields are missing string category = string.IsNullOrEmpty(product.Category) ? "GEN" : product.Category; string brand = string.IsNullOrEmpty(product.Brand) ? "BRD" : product.Brand; string name = string.IsNullOrEmpty(product.Name) ? "PRD" : product.Name; // Extract the first three letters of each, padding if necessary string catPrefix = category.Length >= 3 ? category.Substring(0, 3).ToUpper() : category.ToUpper().PadRight(3, 'X'); string brandPrefix = brand.Length >= 3 ? brand.Substring(0, 3).ToUpper() : brand.ToUpper().PadRight(3, 'X'); string prodPrefix = name.Length >= 3 ? name.Substring(0, 3).ToUpper() : name.ToUpper().PadRight(3, 'X'); // Use the year from CreatedDate and the generated Id int year = product.CreatedDate.Year; int id = product.Id; // Assemble SKU with hyphen separators return $"{catPrefix}-{brandPrefix}-{prodPrefix}-{year}-{id}"; } } }
Now, run the application and test the functionalities; it should work as expected.
Drawbacks of Not Using AutoMapper:
Without AutoMapper, we need to manually map each field from the entity to the DTO and vice versa. For example, in the controller’s GET method, retrieve all products and then manually loop through them to construct a list of ProductDTO objects. Similarly, when creating or updating products, manually map the DTO’s properties back to the entity before saving changes to the database. Although the manual mapping solution above works, it has several drawbacks:
- Repetitive Code: Every time we map between Product and ProductDTO, we need to manually write the mapping code, leading to repetitive and error-prone code. It also increases code length and reduces readability.
- Maintenance Overhead: When models change (e.g., adding a new property), we must update every mapping instance across the codebase. There is a higher chance of missing updates, leading to bugs.
- Error-Prone: Manual mapping increases the risk of errors. For example, accidentally omitting a property or assigning the wrong value can lead to data inconsistencies and hard-to-find bugs.
- DRY Violation: Repeating the mapping logic in multiple places leads to duplicated code. For example, mapping logic in both GetProducts and GetProductById methods results in duplicated code that makes the application harder to maintain. The “Don’t Repeat Yourself” principle is compromised by duplicating mapping logic throughout controllers.
This is where the Automapper will come into the picture
What is Automapper in ASP.NET Core Web API?
AutoMapper is an object-to-object mapping library that automates this process. It helps us define mapping configurations in a single place (Mappin Profile) and use them throughout our application. This leads to cleaner, more maintainable code and reduces the risk of human error. So, AutoMapper:
- Eliminates most of the manual property-by-property mapping.
- It uses conventions (like matching property names and types) to map automatically.
- Allows us to configure any special mapping logic in profiles.
In the context of an ASP.NET Core Web API, Automapper can significantly reduce the amount of code needed to map data between models and DTOs (Data Transfer Objects) and vice versa. For a better understanding, please have a look at the following diagram:
How Do We Use AutoMapper in ASP.NET Core Web API?
We need to follow the below steps to use Automapper in ASP.NET Core Web API.
- Install the Package: Install AutoMapper along with its dependency injection extensions via NuGet:
- Create Mapping Profiles: A mapping profile is a class inherited from Profile, where you define all your mapping configurations.
- Register AutoMapper: Register AutoMapper in your DI container to automatically scan for profiles.
- Inject and Use IMapper: The injected IMapper instance performs object-to-object mapping in our controllers or services.
Step1: Installing AutoMapper
To use AutoMapper, we need to install AutoMapper.Extensions.Microsoft.DependencyInjection NuGet Package. This package is dependent on AutoMapper, so it will also be installed. So, please execute the following command in the Package Manager Console to install the AutoMapper Package.
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
Step2: Create a Mapping Profile
The Mapping Profile in AutoMapper is a class where we need to define the mappings between source and destination types. Within the profile, we need to use the CreateMap<TSource, TDestination>() generic method to specify how the two types should be mapped.
So, first, create a folder named MappingProfiles, and within the MappingProfiles folder, create a class file named ProductMappingProfile.cs. Then, copy and paste the following code. This profile defines how to map between Product and ProductDTO (including renaming properties) and between ProductCreateDTO and Product.
using AutomapperDemo.DTOs; using AutomapperDemo.Models; using AutoMapper; namespace AutomapperDemo.MappingProfiles { public class ProductMappingProfile : Profile { public ProductMappingProfile() { // The CreateMap method create a mapping configuration between the Product entity and the ProductDTO. // This configuration tells AutoMapper how to convert a Product instance (source) // into a ProductDTO instance (destination). CreateMap<Product, ProductDTO>() // The ForMember method configures a specific member mapping. // The following code explicitly maps the 'Name' property of the Product (source) to the 'ProductName' // property of the ProductDTO (destination). .ForMember( dest => dest.ProductName, // Destination member: ProductDTO.ProductName opt => opt.MapFrom(src => src.Name) // Mapping logic: take the value from Product.Name ) // Similarly, the following code explicitly maps the 'Description' property of the Product (source) // to the 'ShortDescription' property of the ProductDTO (destination). .ForMember( dest => dest.ShortDescription, // Destination member: ProductDTO.ShortDescription opt => opt.MapFrom(src => src.Description) // Mapping logic: take the value from Product.Description ); // Create a mapping configuration between the ProductCreateDTO and the Product entity. // This is used when a new product is being created from the data provided by an admin or internal source. // AutoMapper will automatically map properties with matching names and types. CreateMap<ProductCreateDTO, Product>(); } } }
Step3: Register AutoMapper in Program.cs
Register AutoMapper with the dependency injection container. So, please modify the Program class as follows:
using AutomapperDemo.Data; using Microsoft.EntityFrameworkCore; namespace AutomapperDemo { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers() // Optionally, configure JSON options or other formatter settings .AddJsonOptions(options => { // Configure JSON serializer settings to keep the Original names in serialization and deserialization options.JsonSerializerOptions.PropertyNamingPolicy = null; }); // Register AutoMapper (scans the assembly for Profile) // This scans the assembly containing Program class for any classes inheriting Profile // and registers them automatically. builder.Services.AddAutoMapper(typeof(Program).Assembly); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // Register DbContext builder.Services.AddDbContext<ProductDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("ECommerceDBConnection"))); 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(); } } }
The following statement within the Program class registers auto-mapper services to the dependency injection container.
builder.Services.AddAutoMapper(typeof(Program).Assembly);
The above statement scans the entire assembly containing the Program class for any classes inherited from Profile (i.e., AutoMapper profiles). This is useful when we have multiple profiles in different files or folders within our project.
Step4: Using AutoMapper in Products Controller:
AutoMapper is typically used to inject IMapper into our classes (e.g., controllers, services). This interface provides the mapping functionality that can be used to map objects. To map an object from one type to another, we need to use the Map<TDestination>(object source) generic method of the IMapper instance. AutoMapper uses the configuration defined in profiles to perform the mapping.
With AutoMapper registered and mapping profiles configured, the controller code is simplified. AutoMapper automatically handles the conversion based on our profile configuration. So, modify the Products Controller as follows:
using AutoMapper; using AutomapperDemo.Data; using AutomapperDemo.DTOs; using AutomapperDemo.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; namespace AutomapperDemo.Controllers { [Route("api/[controller]")] [ApiController] public class ProductsController : ControllerBase { private readonly ProductDbContext _context; private readonly IMapper _mapper; public ProductsController(ProductDbContext context, IMapper mapper) { _context = context; _mapper = mapper; } // GET: api/Products/GetProducts [HttpGet("GetProducts")] public async Task<ActionResult<List<ProductDTO>>> GetProducts() { var products = await _context.Products.AsNoTracking().ToListAsync(); // AutoMapper automatically maps the list of Products to a list of ProductDTOs var productDTOs = _mapper.Map<List<ProductDTO>>(products); return Ok(productDTOs); } // GET: api/Products/GetProductbyId/{id} [HttpGet("GetProductbyId/{id}")] public async Task<ActionResult<ProductDTO>> GetProductbyId(int id) { var product = await _context.Products.AsNoTracking().FirstOrDefaultAsync(prd => prd.Id == id); if (product == null) return NotFound(); // AutoMapper mapping from Product to ProductDTO var productDTO = _mapper.Map<ProductDTO>(product); return Ok(productDTO); } // POST: api/Products/AddProduct [HttpPost("AddProduct")] public async Task<ActionResult<ProductDTO>> AddProduct(ProductCreateDTO productCreateDTO) { if (productCreateDTO == null) return BadRequest("Invalid product data."); // Map the DTO to a Product entity var product = _mapper.Map<Product>(productCreateDTO); // Determine availability based on stock quantity product.IsAvailable = product.StockQuantity > 0; // CreatedDate as Cuurent Date product.CreatedDate = DateTime.Now; // First, add the product without SKU to generate the Product.Id _context.Products.Add(product); await _context.SaveChangesAsync(); // Now that the product.Id is generated, create the SKU using the new logic: product.SKU = GenerateSKU(product); // Update the product record with the new SKU await _context.SaveChangesAsync(); // AutoMapper mapping from Product to ProductDTO var productDTO = _mapper.Map<ProductDTO>(product); return Ok(productDTO); } // Generates a SKU based on: // - First three letters of Category // - First three letters of Brand // - First three letters of Product Name // - Year of CreatedDate // - Product.Id // Example: If Category="Electronics", Brand="Samsung", Name="Galaxy", CreatedDate is 2025 and Id is 15, // The SKU would be "ELE-SAM-GAL-2025-15". private string GenerateSKU(Product product) { // Use default values if any fields are missing string category = string.IsNullOrWhiteSpace(product.Category) ? "GEN" : product.Category; string brand = string.IsNullOrWhiteSpace(product.Brand) ? "BRD" : product.Brand; string name = string.IsNullOrWhiteSpace(product.Name) ? "PRD" : product.Name; // Extract the first three letters of each, padding if necessary string catPrefix = category.Length >= 3 ? category.Substring(0, 3).ToUpper() : category.ToUpper().PadRight(3, 'X'); string brandPrefix = brand.Length >= 3 ? brand.Substring(0, 3).ToUpper() : brand.ToUpper().PadRight(3, 'X'); string prodPrefix = name.Length >= 3 ? name.Substring(0, 3).ToUpper() : name.ToUpper().PadRight(3, 'X'); // Use the year from CreatedDate and the generated Id int year = product.CreatedDate.Year; int id = product.Id; // Assemble SKU with hyphen separators return $"{catPrefix}-{brandPrefix}-{prodPrefix}-{year}-{id}"; } } }
Testing the Endpoints:
Endpoint 1: GET api/Products/GetProducts
This endpoint retrieves a list of products.
Method: GET
URL: https://localhost:5001/api/Products/GetProducts
Expected Response: A JSON array of products. You should see details such as product names, prices, categories, and other client‑friendly properties.
Endpoint 2: GET api/Products/GetProductbyId/{id}
This endpoint retrieves a single product by its ID.
Method: GET
URL: https://localhost:5001/api/Products/GetProductbyId/1
Expected Response: A JSON object containing the product details with ID 1. You should see a 404 Not Found status if the product does not exist.
Endpoint 3: POST api/Products/AddProduct
This endpoint adds a new product. It expects a JSON body that conforms to your ProductCreateDTO model.
Method: POST
URL: https://localhost:5001/api/Products/AddProduct
Headers: Content-Type: application/json
Request Body (Raw JSON):
{ "Name": "Wireless Mouse", "Description": "Ergonomic wireless mouse", "Price": 29.99, "Category": "Electronics", "Brand": "Tech Brand", "SupplierCost": 50.00, "SupplierInfo": "Internal Supplier Inc.", "StockQuantity": 100 }
Expected Response: The API should return a 201 Created status along with the details of the created product. The response might include the generated product ID and any other information your controller returns.
Handling Mismatched Property Names
When the Source and Destination property names and types are the same, the auto mapper automatically maps them. However, when the property names differ (for example, Name vs. ProductName and Description vs. ShortDescription), AutoMapper will not automatically map these properties. That’s why we used the ForMember method in our mapping profile. The ForMember method explicitly tells AutoMapper how to map those properties. With this configuration in place, the transformed output will have the correct values.
Benefits with AutoMapper
The following are the Key Benefits of using Automapper:
- Eliminates Repetitive Mapping Code: All mapping logic is centralized in one profile.
- Easier Maintenance: Changes to model properties require updating only the profile.
- Consistency: The same mapping configuration is applied throughout the application, reducing errors.
- Reduces Errors: AutoMapper enforces consistent property mapping and easily maps properties even when the source and destination names differ (with configuration).
AutoMapper streamlines object-to-object mapping, removing repetition and reducing errors. With a well-structured DTO approach, we protect sensitive fields, improve performance, and maintain a clear separation of concerns.
In the next article, I will discuss Automapper Complex Mapping in ASP.NET Core Web API with Examples. In this article, I explain How to Use AutoMapper in ASP.NET Core Web API with Examples. I hope you enjoy this article, “AutoMapper in ASP.NET Core Web API.”
Please do continue to update these articles.
It’s very very helpful.
Helping a lot to understand most of the topics.
Thanks.
thank u