Automapper Reverse Mapping in ASP.NET Core Web API

AutoMapper Reverse Mapping in ASP.NET Core Web API

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

AutoMapper Reverse Mapping in ASP.NET Core Web API:

Reverse Mapping in AutoMapper refers to the capability to map properties in both directions: from the source type to the destination type and from the destination type back to the source type. This is useful in scenarios where you need to update your source object based on the changes made to the destination object, such as updating a domain model with the values from a DTO. AutoMapper can configure reverse mappings to automate this process.

To implement reverse mapping in AutoMapper, you need to use the ReverseMap() method in your mapping configuration. This method creates a bidirectional mapping between the two specified types, allowing you to map properties back and forth with a single configuration.

Example to Understand AutoMapper Reverse Mapping in ASP.NET Core Web API:

Let us understand AutoMapper Reverse Mapping in ASP.NET Core Web API with an example. We are going to use the following three classes for this demo.

Address Model

Create a class file named Address.cs, and then copy and paste the following code. This is a very simple class having 4 primitive-type properties.

namespace AutomapperDemo.Models
{
    public class Address
    {
        public int Id { get; set; } 
        public string City { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
    }
}
Employee Model:

Next, create a class file named Employee.cs and then copy and paste the following code. This is a very simple class having 4 primitive-type properties and 1 reference-type property.

namespace AutomapperDemo.Models
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string Gender { get; set; }
        public Address Address { get; set; }
    }
}
EmployeeDTO Model

Finally, create a class file named EmployeeDTO.cs and then copy and paste the following code. The following class contains the combined properties of the Employee and Address classes.

namespace AutomapperDemo.Models
{
    public class EmployeeDTO
    {
        public int EmployeeId { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string Gender { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
    }
}
How Do We Implement AutoMapper Reverse Mapping in ASP.NET Core Web API?

To implement the AutoMapper Reverse Mapping, we need to call the ReverseMap method at the end of the Mapping. So, create a class file with the name MyMappingProfile.cs and copy and paste the following code.

using AutoMapper;
namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings
            CreateMap<Employee, EmployeeDTO>()
                //EmployeeId is different so map using For Member and MapFrom Method
                .ForMember(dest => dest.EmployeeId, act => act.MapFrom(src => src.Id))

                //Address is a Complex type,
                //So, Map Address Object to Simple type using For Member and MapFrom Method
                .ForMember(dest => dest.City, act => act.MapFrom(src => src.Address.City))
                .ForMember(dest => dest.State, act => act.MapFrom(src => src.Address.State))
                .ForMember(dest => dest.Country, act => act.MapFrom(src => src.Address.Country))

                //Call the ReverseMap method to Make the Mapping Bi-Directional
                .ReverseMap();
        }
    }
}
In the above code:
  • We are Mapping the Source Employee object with the Destination EmployeeDTO object. Further, we are mapping the source object ID with the destination object EmployeeId property using the ForMember method, as the property name is different.
  • Again, Address is a Complex type in the Source Object, and hence, we are mapping that Complex Type properties to Primitive types of the Destination Object using the ForMember method.
  • Finally, after all the Mapping, we are calling the ReverseMap method, which makes the Mapping Bi-Directional. Bi-Directional means we can now make Employee as both the Source and Destination object, and we can also make EmployeeDTO as both the Source and Destination Object as per our requirement.
Register AutoMapper in Program.cs

Register AutoMapper and profiles with the dependency injection container in the Program.cs class file. 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);

Using Automapper Reverse Mapping in a Controller

Now, implement a controller that uses AutoMapper Reverse Mapping. So, create an Empty API Controller named Employee and then copy and paste the following code. The following code is self-explained, so please go through the comment line for a better understanding.

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

namespace AutomapperDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeeController : ControllerBase
    {
        private readonly IMapper _mapper;

        public EmployeeController(IMapper mapper)
        {
            _mapper = mapper;
        }

        private List<Employee> listEmployees = new List<Employee>()
        {
            new Employee()
            {
                Id = 1, Name = "Anurag", Gender = "Male", Email = "Anurag@Example.com",
                Address = new Address(){Id= 1001, City ="BBSR", State = "Odisha", Country = "India"}
            },
            new Employee()
            {
                Id = 2, Name = "Pranaya", Gender = "Male", Email = "Pranaya@Example.com",
                Address = new Address(){Id= 1002, City ="Mumbai", State = "Maharashtra", Country = "India"}
            },
        };

        //Return All Employees with Address using EmployeeDTO
        //URL: GET api/Employee
        [HttpGet]
        public ActionResult<List<EmployeeDTO>> GetEmployees()
        {
            // Use AutoMapper to map from Employee to EmployeeDTO
            List<EmployeeDTO> employees = _mapper.Map<List<EmployeeDTO>>(listEmployees);

            return Ok(employees);
        }

        //Add a New Employees with Address and return the new Employee using EmployeeDTO
        //URL: POST api/Employee
        //Request Body
        [HttpPost]
        public ActionResult<EmployeeDTO> AddEmployee(EmployeeDTO employeeDTO)
        {
            if (employeeDTO != null && employeeDTO.EmployeeId == 0)
            {
                // Use AutoMapper to map from EmployeeDTO to Employee
                Employee emp = _mapper.Map<Employee>(employeeDTO);

                //Mannually Set the Properties which are not available in DTO
                emp.Id = 3;
                emp.Address.Id = 1003;

                //Adding Employee Object into the Database
                listEmployees.Add(emp);

                //Setting the Employee ID in EmployeeDTO
                employeeDTO.EmployeeId = emp.Id;

                //Returning the EmployeeDTO
                return Ok(employeeDTO);
            }

            //If the Incoming Data is not Valid Return Bad Requestr
            return BadRequest();
        }

        //Update an Existing Employees with Address and return the Updated information
        //URL: PUT api/Employee/1
        //Request Body
        [HttpPut("{EmployeeId}")]
        public ActionResult<Employee> UpdateEmployee(int EmployeeId, EmployeeDTO employeeDTO)
        {
            if (EmployeeId != employeeDTO.EmployeeId)
            {
                return BadRequest();
            }

            var existingEmployee = listEmployees.FirstOrDefault(emp => emp.Id == EmployeeId);
            if (existingEmployee == null)
            {
                return NotFound();
            }

            // Reverse mapping: DTO -> Entity
            _mapper.Map(employeeDTO, existingEmployee);

            //Next Update the existingEmployee into the Database

            //For the demonstration we are simply returning the updated employee information
            return Ok(existingEmployee);
        }
    }
}
Testing the APIs:

Let us test the above three endpoints using Postman and see the results.

API 1: Return All Employees with Addresses using EmployeeDTO

URL: api/Employee

Method: GET

Using Postman:

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

API 2: Add a New Employee with an Address and return the new Employee using EmployeeDTO

URL: api/Employee

Method: POST

Request Body:

{
  "name": "Mahesh",
  "email": "Mahesh@Example.com",
  "gender": "Male",
  "city": "CTC",
  "state": "Odisha",
  "country": "India"
}
Using Postman:

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

API 3: Update an Existing Employee with an Address and return the Updated information

URL: api/Employee/1

Method: PUT

Request Body:

{
    "employeeId": 1,
    "name": "Hina",
    "email": "Hina@Example.com",
    "gender": "Female",
    "city": "BBSR-Changed",
    "state": "Odisha-Changed",
    "country": "India"
}
Using Postman:

How to Implement AutoMapper Reverse Mapping

Modifying Employee and EmployeeDTO Class:

Let us modify the Employee and EmployeeDTO classes as follows. Now, we need to move the Complex Type Property Address into the EmployeeDTO class and primitive type properties into the Employee class.

So, first, modify the Employee class as follows. Here, you can see we have removed the complex Address property and added primitive properties to hold the Address data.

namespace AutomapperDemo.Models
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string Gender { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
    }
}

Next, modify the EmployeeDTO class as follows. Here, you can see we have removed the primitive properties and added a complex Address Property to hold the employee’s Address data.

namespace AutomapperDemo.Models
{
    public class EmployeeDTO
    {
        public int EmployeeId { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public string Gender { get; set; }
        public Address Address { get; set; }
    }
}

Finally, modify the Address entity as follows:

namespace AutomapperDemo.Models
{
    public class Address
    {
        public string City { get; set; }
        public string State { get; set; }
        public string Country { get; set; }
    }
}
Modifying the Mapper Configuration:

Let’s implement the AutoMapper ReverseMap() function and see whether we get the results as expected or not. Modify the MyMappingProfile class as follows. Here, you can see that after the Mapping, we are calling the ReverseMap method, which basically sets the Mapping between Employee and EmployeeDTO as Two-Way mapping or, you can say, Bi-Directional Mapping.

using AutoMapper;
namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings
            CreateMap<Employee, EmployeeDTO>()
                //EmployeeId is different so map using For Member and MapFrom Method
                .ForMember(dest => dest.EmployeeId, act => act.MapFrom(src => src.Id))

                //Address is a Complex type,
                //Mapping Simple Type to Address type
                .ForMember(dest => dest.Address, act => act.MapFrom(src => new Address()
                {
                    City = src.City,
                    State = src.State,
                    Country = src.Country
                }))

                //Call the ReverseMap method to Make the Mapping Bi-Directional
                .ReverseMap();
        }
    }
}
Modify the Employee Controller:

Next, modify the Employee Controller as follows:

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

namespace AutomapperDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeeController : ControllerBase
    {
        private readonly IMapper _mapper;

        public EmployeeController(IMapper mapper)
        {
            _mapper = mapper;
        }

        private List<Employee> listEmployees = new List<Employee>()
        {
            new Employee()
            {
                Id = 1, Name = "Anurag", Gender = "Male", Email = "Anurag@Example.com",
                 City ="BBSR", State = "Odisha", Country = "India"
            },
            new Employee()
            {
                Id = 2, Name = "Pranaya", Gender = "Male", Email = "Pranaya@Example.com",
                 City ="Mumbai", State = "Maharashtra", Country = "India"
            },
        };

        //Return All Employees with Address using EmployeeDTO
        //URL: GET api/Employee
        [HttpGet]
        public ActionResult<List<EmployeeDTO>> GetEmployees()
        {
            // Use AutoMapper to map from Employee to EmployeeDTO
            List<EmployeeDTO> employees = _mapper.Map<List<EmployeeDTO>>(listEmployees);

            return Ok(employees);
        }

        //Add a New Employees with Address and return the new Employee using Employee 
        //We are returning Employee to check whether the reverse mapping is working or not
        //URL: POST api/Employee
        //Request Body
        [HttpPost]
        public ActionResult<Employee> AddEmployee(EmployeeDTO employeeDTO)
        {
            if (employeeDTO != null && employeeDTO.EmployeeId == 0)
            {
                // Use AutoMapper to map from EmployeeDTO to Employee
                Employee emp = _mapper.Map<Employee>(employeeDTO);

                //Mannually Set the Properties which are not aviable in DTO
                emp.Id = 3;

                //Adding Employee Object into the Database
                listEmployees.Add(emp);

                //Setting the Employee ID in EmployeeDTO
                employeeDTO.EmployeeId = emp.Id;

                //Returning the EmployeeDTO
                return Ok(emp);
            }

            //If the Incoming Data in not Valid Return Bad Requestr
            return BadRequest();
        }

        //Update an Existing Employees with Address and return the Updated information
        //URL: PUT api/Employee/1
        //Request Body
        [HttpPut("{EmployeeId}")]
        public ActionResult<Employee> UpdateEmployee(int EmployeeId, EmployeeDTO employeeDTO)
        {
            if (EmployeeId != employeeDTO.EmployeeId)
            {
                return BadRequest();
            }

            var existingEmployee = listEmployees.FirstOrDefault(emp => emp.Id == EmployeeId);
            if (existingEmployee == null)
            {
                return NotFound();
            }

            // Reverse mapping: DTO -> Entity
            _mapper.Map(employeeDTO, existingEmployee);

            //Next Update the existingEmployee into the Database

            //For the demonstration we are simply returning the updated employee information
            return Ok(existingEmployee);
        }
    }
}

Now, let us test the above three endpoints and see whether the reverse mapping is working this time or not when the destination type contains the complex object mapping with the primitive types of the source object.

Testing the APIs:

Let us test the above endpoints using Postman and see the results. We don’t need to check the GET API as it will work as expected, as we are not doing reverse mapping in the GET Request.

API: Add a New Employee

URL: api/Employee

Method: POST

Request Body:

{
  "name": "Hina",
  "email": "Hina@Example.com",
  "gender": "Female",
  "address": {
    "city": "CTC",
    "state": "Odisha",
    "country": "India"
  }
}
Using Postman:

AutoMapper Reverse Mapping in ASP.NET Core Web API

As you can see in the above response, city, state, and country values are null, meaning these values are not mapped in reverse mapping from the EmployeeDTO object to the Employee object.

API: Update an Existing Employee

URL: api/Employee/1

Method: PUT

Request Body:

{
  "employeeId": 1,
  "name": "Hina-Updated",
  "email": "Hina@Example.com",
  "gender": "Female",
  "address": {
    "city": "CTC-Updated",
    "state": "Odisha-Updated",
    "country": "India"
  }
}
Using Postman:

AutoMapper Reverse Mapping in ASP.NET Core Web API

As you can see in the above response, the name is updated, but the city and state values are not updated; they still hold the old values. That means these values are not mapped in reverse mapping from the EmployeeDTO object to the Employee object.

So, the point you need to remember is that AutoMapper Reverse Mapping works as expected for the primitive types but not for the complex types. So when both classes have members that are named equally, then the AutoMapper ReverseMap() function works as expected. But if the classes contain members that are different and not mapped via the default (per naming) mapping, then this does not work as expected.

How Do We Make the Two-Way Mapping Work as Expected?

If you want to make the two-way mapping work as expected then you need to do the Mapping via ForMember and need to tell the mapping for the complex type. So, modify the MyMappingProfile class as follows. Here, you can see after the ReverseMap method, we are also using the ForMember method to map the Complex Type Property of the EmployeeDTO Object to the Primitive Type Properties of the Employee Object.

using AutoMapper;
namespace AutomapperDemo.Models
{
    public class MyMappingProfile : Profile
    {
        public MyMappingProfile()
        {
            //Configure the Mappings
            CreateMap<Employee, EmployeeDTO>()
                //EmployeeId is different so map using For Member and MapFrom Method
                .ForMember(dest => dest.EmployeeId, act => act.MapFrom(src => src.Id))

                //Address is a Complex type,
                //Mapping  Primitive Type Properties to Complex type
                .ForMember(dest => dest.Address, act => act.MapFrom(src => new Address()
                {
                    City = src.City,
                    State = src.State,
                    Country = src.Country
                }))

                //Call the ReverseMap method to Make the Mapping Bi-Directional
                .ReverseMap()

                //Mappping Complex Type to Primitive Type Properties
                .ForMember(dest => dest.City, act => act.MapFrom(src => src.Address.City))
                .ForMember(dest => dest.State, act => act.MapFrom(src => src.Address.State))
                .ForMember(dest => dest.Country, act => act.MapFrom(src => src.Address.Country));
        }
    }
}

With the above changes in place, now run the application, and you should get the result as expected as expected.

When to Use AutoMapper Reverse Mapping in ASP.NET Core Web API?

AutoMapper is a library that simplifies the mapping between objects of different types, primarily between your domain or entity models and your DTOs (Data Transfer Objects). Reverse mapping is a feature of AutoMapper that allows you to map from your DTOs back to your domain models. This is useful in a variety of scenarios when working with ASP.NET Core Web APIs. Here are some situations when you might use the Automapper Reverse Mapping feature:

  • Updating Existing Entities: In CRUD (Create, Read, Update, Delete) operations, reverse mapping is particularly useful for the update scenarios. When a client sends a DTO with updated data, you can map this DTO back to your domain model to apply these updates to an existing entity before saving changes to the database.
  • Simplifying Model Conversion: Reverse mapping simplifies the conversion process, reducing the need to assign property values from DTOs to domain models manually. This is especially beneficial in complex object graphs where manual mapping can be error-prone and tedious.
  • Maintaining Code Consistency: By using AutoMapper for both mapping to and from your DTOs, you maintain consistency in your codebase. This consistency helps in understanding and maintaining the code, especially for new developers joining the project.
  • Handling Bulk Operations: When dealing with bulk operations that involve multiple objects that need to be updated or created, reverse mapping can automate much of the repetitive work involved in converting arrays or lists of DTOs back to domain models.

In the next article, I will discuss How to Implement AutoMapper Pre-Condition Mapping in ASP.NET Core Web API with Examples. In this article, I explain AutoMapper Reverse Mapping in ASP.NET Core Web API with Examples. I hope you enjoy this article, “AutoMapper Reverse Mapping in ASP.NET Core Web API.”

Leave a Reply

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