Introduction
AutoMapper is an object – to – object mapping which is used to map dissimilar objects with less hassle. One of the most common scenarios for Automapper is when trying to convert an actual object to a DTO object.
To give you a better example, consider the following code:
var Department = GetDepartmentDetails();
DepartmentDetailsDTO obj = new DepartmentDetailsDTO()
{
obj.Name = Department.Name,
obj.Id = Department.Id,
...
...
}
Imagine the number of lines of code wasted just to ignore a few properties of the main object. So here is where AutoMapper comes in handy by replacing the above block of code into a single line!
In this article, we are going to look at how to set up Automapper in your project and use some of its most popular features. I will be showing on a .NET ASP Core 2.1 API project.
Background
An idea of APIs and dependency injection is required since we are going to inject the mapper object into our controller. Some basic understanding of LINQ is preferred.
Initial Setup
First, let's create a new .NET ASP core:
Next up, let's add the NuGet Package for Automapper. Here, we are going to the dependency injection package called AutoMapper.Extensions.Microsoft.DependencyInjection
as shown in the image below:
Now that AutoMapper is installed, we have to configure the startup.cs class to let it know that we are going to inject automapper.
This is a simple one line of code which is shown below inside the ConfigureServices
method:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddAutoMapper();
}
Mapping Profiles
We must add mapping profiles to our projects to tell Automapper the correct way of mapping objects.
A mapping profile class always extends a Profile
class. When the program runs for the first time, Automapper finds the classes which inherit from Profile
class and loads up the mapping configuration.
Automapper automatically maps the same named properties from source and destination.
Let's create a new folder called Mappings and add a new mapping class to it and name it SimpleMappings.cs.
namespace AutoMapper.Mappings
{
public class SimpleMappings : Profile
{
}
}
Time to add the DTO folder and the Classes folder and we shall populate them with the appropriate classes:
namespace AutoMapper.Classes
{
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public string Owner { get; set; }
public string SecretProperty { get; set; }
}
}
and here is the DTO class:
namespace AutoMapper.DTO
{
public class DepartmentDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string Owner { get; set; }
}
}
With these in place, let’s go back and modify our MappingProfiles.cs:
namespace AutoMapper.Mappings
{
public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<Department, DepartmentDTO>().ReverseMap();
}
}
}
Note that specifying ReverseMap()
allows us to map both ways.
Finally, time to add a new controller and let's call it MappingController
:
namespace AutoMapper.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MappingController : Controller
{
private readonly IMapper _mapper;
private readonly Department sampleDepartment;
public MappingController(IMapper mapper)
{
_mapper = mapper;
sampleDepartment = new Department()
{
Name = "department1",
Id = 1,
Owner = "ABC",
SecretProperty = "Very secret property"
};
}
[HttpGet]
public ActionResult<DepartmentDTO> Get()
{
return _mapper.Map<DepartmentDTO>(sampleDepartment);
}
}
}
Here is the result obtained from postman when called to localhost:<port>/api/mapping:
Our secret property is not shown meaning our automapper is working fine.
Getting Deeper
In the above example, it was a simple one to one mapping but automapper is also used for object flattening or making complex objects from a flattened object.
For example, let's consider a Person
and personDTO
object as the one below, the Person
object has a property Address
which is a type of Address
object. Here, we are going to create a flattened object from complex object.
namespace AutoMapper.Classes
{
public class Person
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
public Address Address { get; set; }
}
}
Address.cs class:
namespace AutoMapper.Classes
{
public class Address
{
public string HouseNumber { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
}
PersonDTO.cs is going to contain the city
property of the Address
object only.
namespace AutoMapper.DTO
{
public class PersonDTO
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public string City { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
}
}
Now, we have to head back to the mapping profile
class and explicitly tell automapper to map the city
property from Address
's city
property. This can be done with the help of ForMember
which is shown below in the updated MappingProfiles.cs.
namespace AutoMapper.Mappings
{
public class MappingProfiles : Profile
{
public MappingProfiles()
{
CreateMap<Department, DepartmentDTO>().ReverseMap();
CreateMap<Person, PersonDTO>()
.ForMember(dest => dest.City,
opts => opts.MapFrom(
src => src.Address.City
)).ReverseMap();
}
}
}
Here is the complete MappingController
:
namespace AutoMapper.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MappingController : Controller
{
private readonly IMapper _mapper;
private readonly Department sampleDepartment;
private readonly Person samplePerson;
public MappingController(IMapper mapper)
{
_mapper = mapper;
sampleDepartment = new Department()
{
Name = "department1",
Id = 1,
Owner = "ABC",
SecretProperty = "Very secret property"
};
samplePerson = new Person()
{
Firstname = "John",
Lastname = "Doe",
Age = 25,
Sex = "Male",
Address = new Address()
{
City = "New York City",
HouseNumber = "10",
State = "NY",
ZipCode = "99999"
}
};
}
[HttpGet]
public ActionResult<DepartmentDTO> Get()
{
return _mapper.Map<DepartmentDTO>(sampleDepartment);
}
[HttpGet]
[Route("person")]
public ActionResult<PersonDTO> GetPerson()
{
return _mapper.Map<PersonDTO>(samplePerson);
}
}
}
And finally, here is the output on postman:
The final DTO object is showing the city
. Our mapping configuration worked!