Automapper Complex Mapping in ASP.NET Core Web API

AutoMapper Complex Mapping in ASP.NET Core Web API

In this article, I will discuss How to Implement AutoMapper Complex Mapping in ASP.NET Core Web API Application with Examples. Please read our previous article discussing How to Use Automapper in ASP.NET Core Web API with Examples.

AutoMapper Complex Mapping in ASP.NET Core Web API

When both types (Source and Destination) involved in the mapping contain properties of the complex type, then in such scenarios, we need to use the AutoMapper Complex Mapping in ASP.NET Core Web API. Let us understand AutoMapper Complex Mapping with an example in ASP.NET Core Web API Application.

Assume we have an e-commerce application. We need models for product categories and products. The following are our entity models and DTOs (Data Transfer Objects):

Entity Models:

Create a class file named Category.cs, and then copy and paste the following code.

namespace AutomapperDemo.Models
{
    // Entity Model
    public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Product> Products { get; set; }
    }
}

Create a class file named Product.cs, and then copy and paste the following code.

namespace AutomapperDemo.Models
{
    // Entity Model
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public int CategoryId { get; set; }
    }
}
Data Transfer Objects (DTOs):

Create a class file named CategoryDTO.cs and then copy and paste the following code.

namespace AutomapperDemo.Models
{
    // DTO
    public class CategoryDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<ProductDTO> Products { get; set; }
    }
}

Create a class file named ProductDTO.cs and then copy and paste the following code.

namespace AutomapperDemo.Models
{
    // DTO
    public class ProductDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
    }
}
Understanding AutoMapper Complex Mapping in ASP.NET Core Web API:

Our requirement is to map the Category object with the CategoryDTO object. To make this simple, here we created both classes with the same property names. But the thing that we need to keep in mind here is that we created the Products property as a complex type of collection property.

Next, create a class file with the name MyMappingProfile.cs and copy and paste the following code. Here, we are writing all the mapping code. As you can see, here, we are mapping Category with the CategoryDTO class.

using AutoMapper;

namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings
            CreateMap<Category, CategoryDTO>(); 
        }
    }
}
Register AutoMapper in Program.cs

Register AutoMapper and profiles with the dependency injection container in Program.cs. So, please add the following statement within the Program class. This configuration tells AutoMapper to scan the assembly (or assemblies) for classes that inherit from Profile and automatically register them.

builder.Services.AddAutoMapper(typeof(Program).Assembly);

Implementing the Mapping in a Controller

Now, implement a controller that uses AutoMapper to return a list of categories with their products. So, create an Empty API Controller named Product and then copy and paste the following code:

using AutoMapper;
using AutomapperDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace AutomapperDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        //Create a variable to holder mapper instance
        private readonly IMapper _mapper;

        //Framework will inject the instance using Constructor
        public ProductController(IMapper mapper)
        {
            //Initialize the variable with the injected mapper instance
            _mapper = mapper;
        }

        private List<Category> listCategories = new List<Category>()
        {
           new Category { Id = 1, Name="Electronics", 
               Products = new List<Product> 
               { 
                   new Product { Id = 1001, Name="Laptop", Description="Gaming Laptop", Price = 1000, CategoryId = 1},
                   new Product { Id = 1002, Name="Desktop", Description="Programming Desktop", Price = 2000, CategoryId = 1}
               }
           },
           new Category { Id = 2, Name="Appearl",
               Products = new List<Product>
               {
                   new Product { Id = 1003, Name="T-Shrt", Description="T-Shrt with V Neck", Price = 700, CategoryId = 2},
                   new Product { Id = 1004, Name="Jacket", Description="Winter Jacket", Price = 800, CategoryId = 2}
               }
           }
        };

        [HttpGet("Categories")]
        public ActionResult<List<CategoryDTO>> GetAllCategory()
        {
            List<CategoryDTO> categories = _mapper.Map<List<CategoryDTO>>(listCategories);
            return Ok(categories);
        }
    }
}

Now, run the application and access the above endpoint (/api/product/categories), and you should get the following runtime exception.

How to Implement AutoMapper Complex Mapping in ASP.NET Core Web API with Examples

Now, if you go to the inner exception and check the message property, then it clearly shows that the mapping type configuration is missing for Category and CategoryDTO, as shown in the image below. The following error message indicates that AutoMapper encountered an issue while trying to map from a Category entity to a CategoryDTO data transfer object, specifically with the Products member of the destination type.

How to Implement AutoMapper Complex Mapping in ASP.NET Core Web API

How to Solve the Above Problem?

In order to solve the above problem, you need to configure the mapping between the Category and CategoryDTO along with the Product and ProductDTO Mapping. So, modify the MyMappingProfile.cs class as follows.

using AutoMapper;

namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings
            CreateMap<Category, CategoryDTO>(); 
            CreateMap<Product, ProductDTO>(); 
        }
    }
}

With the above changes in place, run the application, and you should get the output as expected, as shown in the below image:

AutoMapper Complex Mapping in ASP.NET Core Web API

What Happens If We Change the Complex Type Property Name?

Let us understand this with an example. If you see the Category and CategoryDTO classes, then you will see both these classes have the complex property name Products. Let’s change the complex property name Products to ProductsDTO in the CategoryDTO class. So, modify the CategoryDTO class as follows.

namespace AutomapperDemo.Models
{
    // DTO
    public class CategoryDTO
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<ProductDTO> ProductsDTO { get; set; }
    }
}

Now, if you run the application and access the endpoint (/api/product/categories), you will see the ProductsDTO is null, as shown in the image below.

What Happens If We Change the Complex Type Property Name?

How to Solve the Above Problem?

To solve the above problem, we need to map the Products property of the Category object to the ProductsDTO property of the CategoryDTO object in the mapping profile using the ForMember method. So, modify the MyMappingProfile.cs class as follows.

using AutoMapper;

namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings
            CreateMap<Category, CategoryDTO>()
                .ForMember(dest => dest.ProductsDTO, act => act.MapFrom(src => src.Products));
            
            CreateMap<Product, ProductDTO>();
        }
    }
}

With the above changes in place, run the application and access the endpoint (/api/product/categories), and you will see the ProductsDTO object data as shown in the below image.

What Happens If We Change the Complex Type Property Name?

What Happens if the Complex Type Property Names Are Different?

Let us understand this with an example. Let’s modify the property names in the ProductDTO class, as shown below. As you can see, we have changed the Name and Price properties to ProductName and ProductPrice.

namespace AutomapperDemo.Models
{
    // DTO
    public class ProductDTO
    {
        public int Id { get; set; }
        public string ProductName { get; set; }
        public string Description { get; set; }
        public decimal ProductPrice { get; set; }
    }
}

With the above change in place, run the application and access the endpoint (/api/product/categories), and you will get the following response.

What Happens if the Complex Type Property Names Are Different?

Here, you can see the ProductName and ProductPrice data are missing. This is because these property values are not mapped from the source object to the destination object, as the property names are different. To map the above two properties, we need to change the MyMappingProfile.cs class as follows.

using AutoMapper;

namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings
            CreateMap<Category, CategoryDTO>()
                .ForMember(dest => dest.ProductsDTO, act => act.MapFrom(src => src.Products));
            
            CreateMap<Product, ProductDTO>()
                .ForMember(dest => dest.ProductName, act => act.MapFrom(src => src.Name))
                .ForMember(dest => dest.ProductPrice, act => act.MapFrom(src => src.Price));
        }
    }
}

With the above change in place, run the application and access the endpoint (/api/product/categories), and you should get the response as expected, as shown in the image below:

Automapper Complex Mapping in ASP.NET Core Web API with Examples

In the next article, I will discuss Mapping Complex Types to Primitive Types using AutoMapper in ASP.NET Core Web API with Examples. In this article, I explain Automapper Complex Mapping in ASP.NET Core Web API with Examples. I hope you enjoy this article, “Automapper Complex Mapping in ASP.NET Core Web API.”

Leave a Reply

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