C# – AutoMapper

By | 29/06/2022

In this post, we will see how to configure and use AutoMapper in a Web API project.
But first of all, what is AutoMapper?
From official web site:
AutoMapper is an object-object mapper. Object-object mapping works by transforming an input object of one type into an output object of a different type. What makes AutoMapper interesting is that it provides some interesting conventions to take the dirty work out of figuring out how to map type A to type B. As long as type B follows AutoMapper’s established convention, almost zero configuration is needed to map two types.

We start creating a Web API project called TestAutoMapper and then, we add an entity called Person, an entity called PersonDTO and a class called Core that will be our business layer:

[PERSON.CS]

using System;

namespace TestAutoMApper.Entities
{
    public class Person
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime DateBirth { get; set; }
    }
}



[PERSONDTO.CS]

namespace TestAutoMApper.Entities
{
    public class PersonDTO
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}



[ICORE.CS]

using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public interface ICore
    {
        List<PersonDTO> GetListPeople();
    }
}



[CORE.CS]

using System;
using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public class Core : ICore
    {
        private readonly List<Person> lstPerson = null;

        public Core()
        {
            lstPerson = new List<Person>();
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_1",
                FirstName = "FirstName_1",
                DateBirth = DateTime.Now.AddYears(-34).AddDays(100)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_2",
                FirstName = "FirstName_2",
                DateBirth = DateTime.Now.AddYears(-20).AddDays(60)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_3",
                FirstName = "FirstName_3",
                DateBirth = DateTime.Now.AddYears(-54).AddDays(-54)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_4",
                FirstName = "FirstName_4",
                DateBirth = DateTime.Now.AddYears(-41).AddDays(-3)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_5",
                FirstName = "FirstName_5",
                DateBirth = DateTime.Now.AddYears(-27).AddDays(37)
            });
        }

        public List<PersonDTO> GetListPeople()
        {
            // Without Automapper, we should read all items of lstPerson 
            // and create another List of PersonDTO

            List<PersonDTO> result = new List<PersonDTO>();

            lstPerson.ForEach(x =>
            {
                PersonDTO objPerson = new PersonDTO { 
                    FirstName = x.FirstName,
                    LastName = x.LastName
                };

                result.Add(objPerson);
            });

            return result;
        }
    }
}



Then, we create a controller called PersonController:

[PERSONCONTROLLER.CS]

using Microsoft.AspNetCore.Mvc;
using TestAutoMApper.BLL;

namespace TestAutoMApper.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PersonController : ControllerBase
    {
        private readonly ICore _core;
        public PersonController(ICore core)
        {
            _core = core;
        }

        [HttpGet]
        public IActionResult Get()
        {
            return Ok(_core.GetListPeople());
        }
    }
}



Finally, we modify the file Startup to define the Dependency Inject for ICore:

[STARTUP.CS]

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using TestAutoMApper.BLL;

namespace TestAutoMApper
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Dependency Injection for ICore
            services.AddSingleton<ICore, Core>();
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestAutoMApper", Version = "v1" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TestAutoMApper v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}



We have done and now, if we run the application, this will be the result:


Everything works fine and now, we will see how to optimize the code using Automapper.

We start installing these two libraries:

Install-Package AutoMapper
Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection



Then, we add a class MapDTO where we will define the mapping between Person and PersonDTO:

[MAPDTO.CS]

using AutoMapper;

namespace TestAutoMApper.Entities
{
    public class MapDTO: Profile
    {
        // in the constructor we define the mapping
        public MapDTO()
        {
            // the properties have the same name 
            // and so, we don't have to specify anything
            CreateMap<Person, PersonDTO>();
        }
    }
}



Now, we change the Core in order to use AutoMapper:

[CORE.CS]

using AutoMapper;
using System;
using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public class Core : ICore
    {
        private readonly List<Person> lstPerson = null;
        private readonly IMapper _mapper;

        // We inject IMapper
        public Core(IMapper mapper)
        {
            lstPerson = new List<Person>();
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_1",
                FirstName = "FirstName_1",
                DateBirth = DateTime.Now.AddYears(-34).AddDays(100)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_2",
                FirstName = "FirstName_2",
                DateBirth = DateTime.Now.AddYears(-20).AddDays(60)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_3",
                FirstName = "FirstName_3",
                DateBirth = DateTime.Now.AddYears(-54).AddDays(-54)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_4",
                FirstName = "FirstName_4",
                DateBirth = DateTime.Now.AddYears(-41).AddDays(-3)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_5",
                FirstName = "FirstName_5",
                DateBirth = DateTime.Now.AddYears(-27).AddDays(37)
            });

            _mapper = mapper;
        }

        public List<PersonDTO> GetListPeople()
        {
            // Mapping between List<Person> and List<PersonDTO>
            return _mapper.Map<List<Person>, List<PersonDTO>>(lstPerson);
        }
    }
}



Finally, in the method ConfigureServices of Startup.cs, we have to register the profile for mapping :

[STARTUP.CS]

public void ConfigureServices(IServiceCollection services)
{
      // Added Dependency Injection for ICore
      services.AddSingleton<ICore, Core>();
      // We register the profile for mapping
      services.AddAutoMapper(typeof(MapDTO));

      services.AddControllers();
      services.AddSwaggerGen(c =>
      {
          c.SwaggerDoc("v1", new OpenApiInfo { Title = "TestAutoMApper", Version = "v1" });
      });
}



We have done and now, if we run the application, this will be the result:



MAPPING OF PROPERTIES WITH DIFFERENT NAME
When we have properties with different names, we have to specify the mapping definitions.
For example:
we create a class called PersonDTO2 with the same properties of PersonDTO but with different names:

[PERSONDTO2.CS]

namespace TestAutoMApper.Entities
{
    public class PersonDTO2
    {
        public string Name { get; set; }
        public string Surname { get; set; }
    }
}



Then, we define the mapping in the class MapDTO:

[MAPDTO.CS]

using AutoMapper;

namespace TestAutoMApper.Entities
{
    public class MapDTO: Profile
    {
        // in the constructor we define the mapping
        public MapDTO()
        {
            // in this case, with the same properties,
            // we don't have to specify anything
            CreateMap<Person, PersonDTO>();

            CreateMap<Person, PersonDTO2>()
                // we have to specify the mapping between PersonDTO2.Name and Person.FirstName
                // dest => destionation  PersonDTO2
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FirstName))
                // we have to specify the mapping between PersonDTO2.Surname and Person.LastName
                .ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.LastName));
        }
    }
}



Now, we add in the class Core a method called GetListPeople2:

[ICORE.CS]

using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public interface ICore
    {
        List<PersonDTO> GetListPeople();
        List<PersonDTO2> GetListPeople2();
    }
}



[CORE.CS]

using AutoMapper;
using System;
using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public class Core : ICore
    {
        private readonly List<Person> lstPerson = null;
        private readonly IMapper _mapper;

        // We inject IMapper
        public Core(IMapper mapper)
        {
            lstPerson = new List<Person>();
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_1",
                FirstName = "FirstName_1",
                DateBirth = DateTime.Now.AddYears(-34).AddDays(100)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_2",
                FirstName = "FirstName_2",
                DateBirth = DateTime.Now.AddYears(-20).AddDays(60)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_3",
                FirstName = "FirstName_3",
                DateBirth = DateTime.Now.AddYears(-54).AddDays(-54)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_4",
                FirstName = "FirstName_4",
                DateBirth = DateTime.Now.AddYears(-41).AddDays(-3)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_5",
                FirstName = "FirstName_5",
                DateBirth = DateTime.Now.AddYears(-27).AddDays(37)
            });

            _mapper = mapper;
        }

        public List<PersonDTO> GetListPeople()
        {
            return _mapper.Map<List<Person>, List<PersonDTO>>(lstPerson);
        }

        public List<PersonDTO2> GetListPeople2()
        {
            return _mapper.Map<List<Person>, List<PersonDTO2>>(lstPerson);
        }
    }
}


Finally, we modify the personcontroller:

[PERSONCONTROLLER.CS]

using Microsoft.AspNetCore.Mvc;
using TestAutoMApper.BLL;

namespace TestAutoMApper.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PersonController : ControllerBase
    {
        private readonly ICore _core;
        public PersonController(ICore core)
        {
            _core = core;
        }

        [HttpGet]
        public IActionResult Get()
        {
            return Ok(_core.GetListPeople2());
        }
    }
}



We have done and now, if we run the application, this will be the result:



MAPPING ELABORATION
Here, we will see how to add an elaboration in the mapping of a property.
We start adding a new class called PersonDTO3 with a property Age that we will map with the property DateBirth of Person:

[PERSONDTO3.CS]

namespace TestAutoMApper.Entities
{
    public class PersonDTO3
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public string Age { get; set; }
    }
}



[MAPDTO.CS]

using AutoMapper;

namespace TestAutoMApper.Entities
{
    public class MapDTO: Profile
    {
        // in the constructor we define the mapping
        public MapDTO()
        {
            // in this case, with the same properties,
            // we don't have to specify anything
            CreateMap<Person, PersonDTO>();

            CreateMap<Person, PersonDTO2>()
                // we have to specify the mapping between PersonDTO2.Name and Person.FirstName
                // dest => destionation  PersonDTO2
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FirstName))
                // we have to specify the mapping between PersonDTO2.Surname and Person.LastName
                .ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.LastName));

            CreateMap<Person, PersonDTO3>()
                // we have to specify the mapping between PersonDTO3.Name and Person.FirstName
                // dest => destionation  PersonDTO2
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FirstName))
                // we have to specify the mapping between PersonDTO3.Surname and Person.LastName
                .ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.LastName))
                // If the person is adult, the system will show the age otherwise, it will show an 'alert' value 
                .ForMember(dest => dest.Age, opt => opt.MapFrom(src => (src.DateBirth < System.DateTime.Now.AddYears(-18) 
                            ? $"The age is: {(System.DateTime.Now.Year - src.DateBirth.Year)}" : 
                            $"Sorry but the user is not adult")));
        }
    }
}



Then, we modify the class Core adding a new Person not adult and adding a new method called GetListPeople3:

[ICORE.CS]

using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public interface ICore
    {
        List<PersonDTO> GetListPeople();
        List<PersonDTO2> GetListPeople2();
        List<PersonDTO3> GetListPeople3();
    }
}



[CORE.CS]

using AutoMapper;
using System;
using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public class Core : ICore
    {
        private readonly List<Person> lstPerson = null;
        private readonly IMapper _mapper;

        // We inject IMapper
        public Core(IMapper mapper)
        {
            lstPerson = new List<Person>();
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_1",
                FirstName = "FirstName_1",
                DateBirth = DateTime.Now.AddYears(-34).AddDays(100)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_2",
                FirstName = "FirstName_2",
                DateBirth = DateTime.Now.AddYears(-20).AddDays(60)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_3",
                FirstName = "FirstName_3",
                DateBirth = DateTime.Now.AddYears(-54).AddDays(-54)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_4",
                FirstName = "FirstName_4",
                DateBirth = DateTime.Now.AddYears(-41).AddDays(-3)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_5",
                FirstName = "FirstName_5",
                DateBirth = DateTime.Now.AddYears(-27).AddDays(37)
            });
            // no adult person
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_6",
                FirstName = "FirstName_6",
                DateBirth = DateTime.Now.AddYears(-16).AddDays(37)
            });

            _mapper = mapper;
        }

        public List<PersonDTO> GetListPeople()
        {
            return _mapper.Map<List<Person>, List<PersonDTO>>(lstPerson);
        }

        public List<PersonDTO2> GetListPeople2()
        {
            return _mapper.Map<List<Person>, List<PersonDTO2>>(lstPerson);
        }

        public List<PersonDTO3> GetListPeople3()
        {
            return _mapper.Map<List<Person>, List<PersonDTO3>>(lstPerson);
        }
    }
}



We have done and now, if we run the application (putting GetListPeople3 in the method Get of PersonController), this will be the result:



SKIPPING MAPPING OF A PROPERTY
When we don’t want to use a property in the mapping, we can simply skip it.
We start to modify the entity PersonDTO2 adding a new property called Id:

[PERSONDTO2.CS]

using System;

namespace TestAutoMApper.Entities
{
    public class PersonDTO2
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
    }
}



Then, we modifiy personcontroller in order to use the method GetListPeople2:
[PERSONCONTROLLER.CS]

using Microsoft.AspNetCore.Mvc;
using TestAutoMApper.BLL;

namespace TestAutoMApper.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PersonController : ControllerBase
    {
        private readonly ICore _core;
        public PersonController(ICore core)
        {
            _core = core;
        }

        [HttpGet]
        public IActionResult Get()
        {
            return Ok(_core.GetListPeople2());
        }
    }
}



We have done and now, if we run the application, this will be the result:

We can see that the property id is valued because the class People has this property as well.
If we don’t want to use this property and not showing the value, we have to modify the mapping in this way:

[MAPDTO.CS]

using AutoMapper;

namespace TestAutoMApper.Entities
{
    public class MapDTO: Profile
    {
        // in the constructor we define the mapping
        public MapDTO()
        {
            // in this case, with the same properties,
            // we don't have to specify anything
            CreateMap<Person, PersonDTO>();

            CreateMap<Person, PersonDTO2>()
                // we have to specify the mapping between PersonDTO2.Name and Person.FirstName
                // dest => destionation  PersonDTO2
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FirstName))
                // we have to specify the mapping between PersonDTO2.Surname and Person.LastName
                .ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.LastName))
                // we ignore the property Id in PersonDTO2 
                .ForMember(dest => dest.Id, opt => opt.Ignore());



            CreateMap<Person, PersonDTO3>()
                // we have to specify the mapping between PersonDTO3.Name and Person.FirstName
                // dest => destionation  PersonDTO2
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FirstName))
                // we have to specify the mapping between PersonDTO3.Surname and Person.LastName
                .ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.LastName))
                .ForMember(dest => dest.Age, opt => opt.MapFrom(src => (src.DateBirth < System.DateTime.Now.AddYears(-18) 
                            ? $"The age is: {(System.DateTime.Now.Year - src.DateBirth.Year)}" : 
                            $"Sorry but the user is not adult")));
        }
    }
}



We have done and now, if we run the application, this will be the result:



REVERSING MAP
Above we have created the map between Person and PersonDTO.
But, does it work if we try to use this mapping from PersonDTO to Person?
The answer is no!
Let’s try!
In the class Core we create a new method called GetListPerson to get the list of Person:

[ICORE.CS]

using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public interface ICore
    {
        List<PersonDTO> GetListPeople();
        List<PersonDTO2> GetListPeople2();
        List<PersonDTO3> GetListPeople3();
        List<Person> GetListPerson();
    }
}



[CORE.CS]

using AutoMapper;
using System;
using System.Collections.Generic;
using TestAutoMApper.Entities;

namespace TestAutoMApper.BLL
{
    public class Core : ICore
    {
        private readonly List<Person> lstPerson = null;
        private readonly IMapper _mapper;

        // We inject IMapper
        public Core(IMapper mapper)
        {
            lstPerson = new List<Person>();
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_1",
                FirstName = "FirstName_1",
                DateBirth = DateTime.Now.AddYears(-34).AddDays(100)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_2",
                FirstName = "FirstName_2",
                DateBirth = DateTime.Now.AddYears(-20).AddDays(60)
            });
            lstPerson.Add(new Person
            {
                Id = Guid.NewGuid(),
                LastName = "LastName_3",
                FirstName = "FirstName_3",
                DateBirth = DateTime.Now.AddYears(-54).AddDays(-54)
            });
            

            _mapper = mapper;
        }

        public List<PersonDTO> GetListPeople()
        {
            return _mapper.Map<List<Person>, List<PersonDTO>>(lstPerson);
        }

        public List<PersonDTO2> GetListPeople2()
        {
            return _mapper.Map<List<Person>, List<PersonDTO2>>(lstPerson);
        }

        public List<PersonDTO3> GetListPeople3()
        {
            return _mapper.Map<List<Person>, List<PersonDTO3>>(lstPerson);
        }

        public List<Person> GetListPerson()
        {
            List<PersonDTO> lstPersonDTO = new List<PersonDTO>() {
                new PersonDTO { FirstName = "FirstNameDTO_1", LastName="LastNameDTO_1" },
                new PersonDTO { FirstName = "FirstNameDTO_2", LastName="LastNameDTO_2" },
                new PersonDTO { FirstName = "FirstNameDTO_3", LastName="LastNameDTO_3" }
            };

            return _mapper.Map<List<PersonDTO>, List<Person>>(lstPersonDTO);
        }
    }
}



Finally, we modify PersonController:

[PERSONCONTROLLER.CS]

using Microsoft.AspNetCore.Mvc;
using TestAutoMApper.BLL;

namespace TestAutoMApper.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PersonController : ControllerBase
    {
        private readonly ICore _core;
        public PersonController(ICore core)
        {
            _core = core;
        }

        [HttpGet]
        public IActionResult Get()
        {
            return Ok(_core.GetListPerson());
        }
    }
}



We have done and now, if we run the application, this will be the result:


In order to fix the error, in the class MapDTO, we have just to insert the method ReverseMap in the mapping between Person and PersonDTO:

[MAPDTO.CS]

using AutoMapper;

namespace TestAutoMApper.Entities
{
    public class MapDTO: Profile
    {
        // in the constructor we define the mapping
        public MapDTO()
        {
            // in this case, with the same properties,
            // we don't have to specify anything
            CreateMap<Person, PersonDTO>()
                .ReverseMap();

            CreateMap<Person, PersonDTO2>()
                // we have to specify the mapping between PersonDTO2.Name and Person.FirstName
                // dest => destionation  PersonDTO2
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FirstName))
                // we have to specify the mapping between PersonDTO2.Surname and Person.LastName
                .ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.LastName));

            

            CreateMap<Person, PersonDTO3>()
                // we have to specify the mapping between PersonDTO3.Name and Person.FirstName
                // dest => destionation  PersonDTO2
                .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FirstName))
                // we have to specify the mapping between PersonDTO3.Surname and Person.LastName
                .ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.LastName))
                .ForMember(dest => dest.Age, opt => opt.MapFrom(src => (src.DateBirth < System.DateTime.Now.AddYears(-18) 
                            ? $"The age is: {(System.DateTime.Now.Year - src.DateBirth.Year)}" : 
                            $"Sorry but the user is not adult")));
        }
    }
}



We have done and now, if we run the application, this will be the result:



Category: C# Tags:

Leave a Reply

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