.NET 6 - AutoMapper & Data Transfer Objects (DTOs) 🗺
Tags: dotnet, api, tutorial, performance
Published: 2022-10-18 10:33:26

###Intro
In this article we will be exploring AutoMapper and Data Transfer Objects (DTOs) in .Net 6 Web Api. You can watch the full video on YouTube

{% embed https://youtu.be/Wm7hN0S-AdU %}

We can start today by explaining what is AutoMapper and why do we need it

AutoMapper is a library that helps us to transform one object type to another in a very easy accurate way.

We start by creating our web API

```bash
dotnet new webapi -n SampleMapper
```

Once we create our application we need to install the AutoMapper nuget package into our application

```bash
dotnet add package AutoMapper --version 12.0.0
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection --version 12.0.0
```

Now that our nuget packages are installed we can start implementing and utilising automapper.

In this example we will be building a simple api which will take DTOs which the client sends and we will be utilising automapper to transform the objs into database obj and vise versa when we get any obj from the data we will do some data transformation to the client so we are not returning the full object

The first thing we need to do is to create a new folder called models which will represent our database tables. Inside the root app directory we add this folder and inside the Models folder we create a new class called Driver and we add the following to it

```csharp
namespace SampleMapper.Models;

public class Driver
{
    public Guid Id { get; set; }
    public string FirstName { get; set; } = "";
    public string LastName { get; set; } = "";
    public int DriverNumber { get; set; }
    public DateTime DateAdded { get; set; }
    public DateTime DateUpdated { get; set; }
    public int Status { get; set; }
    public int WorldChampionships { get; set; }
}
```

Next we need to create a controller which will responsible for handling all of the Driver requests. In order to make this example as simple as possible we will be using an in-memory database instead of a full database.

```csharp
using Microsoft.AspNetCore.Mvc;
using SampleMapper.Models;

namespace SampleMapper.Controllers;

[ApiController]
[Route("[controller]")]
public class DriversController : ControllerBase
{
    private static List<Driver> drivers = new List<Driver>();

    private readonly ILogger<DriversController> _logger;

    public DriversController(ILogger<DriversController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IActionResult GetDrivers()
    {
        var items = drivers.Where(x => x.Status == 1).ToList();
        return Ok(items);
    }

    [HttpPost]
    public IActionResult CreateDriver(Driver data)
    {
        if(ModelState.IsValid)
        {
            drivers.Add(data);

            return CreatedAtAction("GetDriver", new {data.Id}, data);
        }

        return new JsonResult("Something went wrong") {StatusCode = 500};
    }

    [HttpGet("{id}")]
    public IActionResult GetDriver(Guid id)
    {
        var item = drivers.FirstOrDefault(x => x.Id == id);

        if(item == null)
            return NotFound();

        return Ok(item);
    }

    [HttpPut("{id}")]
    public IActionResult UpdateDriver(Guid id, Driver item)
    {
        if(id != item.Id)
            return BadRequest();

        var existItem = drivers.FirstOrDefault(x => x.Id == id);

        if(existItem == null)
            return NotFound();

        existItem.FirstName = item.FirstName;
        existItem.LastName = item.LastName;
        existItem.DriverNumber = item.DriverNumber;
        existItem.WorldChampionships = item.WorldChampionships;
        

        return NoContent();
    }

    [HttpDelete("{id}")]
    public IActionResult DeleteDriver(Guid id)
    {
        var existItem = drivers.FirstOrDefault(x => x.Id == id);

        if(existItem == null)
            return NotFound();

        existItem.Status = 0;

        return Ok(existItem);
    }
}
```

Now let us see how we can transform this into a more optimised API and more user friendly.

Inside the Models folder we create a new folder called DTOs, inside the DTOs folder we create 2 new folders called 

- incoming: responsible for all incoming requests to our API
- outgoing: responsible for all outgoing requests from our API

Will start by the incoming request. Inside the incoming folder we add the following class 

```csharp
namespace SampleMapper.Models.DTOs.Incoming;

public class DriverCreationDto
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int DriverNumber { get; set; }
    public int WorldChampionships { get; set; }
}
```

Now let us update the controller to take advantage of this DTO

```csharp
[HttpPost]
public IActionResult CreateDriver(DriverCreationDto data)
{
    var _driver = new Driver()
    {
        Id = Guid.NewGuid(),
        Status = 1,
        DateAdded = DateTime.Now,
        DateUpdated = DateTime.Now,
        DriverNumber = data.DriverNumber,
        FirstName = data.FirstName,
        LastName = data.LastName,
        WorldChampionships = data.WorldChampionships
    };
    if(ModelState.IsValid)
    {
        drivers.Add(_driver);

        return CreatedAtAction("GetDriver", new {_driver.Id}, data);
    }

    return new JsonResult("Something went wrong") {StatusCode = 500};
}
```

We can see that there is an advantage by using the Dto but it still not what we need, we are still doing manual mapping between 2 objs.

Here we can see how AutoMapper will be able to help us

Inside the rood directory of the application we are going to create a new folder called Profiles, inside the folder we need to create a new class called DriverProfile and update it to the following

```csharp
using AutoMapper;
using SampleMapper.Models;
using SampleMapper.Models.DTOs.Incoming;

namespace SampleMapper.Profiles;

public class DriverProfile : Profile
{
    public DriverProfile()
    {
        CreateMap<DriverCreationDto, Driver>()
            .ForMember(
                dest => dest.Id,
                opt => opt.MapFrom(src => Guid.NewGuid())
            )
            .ForMember(
                dest => dest.FirstName,
                opt => opt.MapFrom(src => $"{src.FirstName} ")
            )
            .ForMember(
                dest => dest.LastName,
                opt => opt.MapFrom(src => $"{src.LastName}")
            )
            .ForMember(
                dest => dest.WorldChampionships,
                opt => opt.MapFrom(src => src.WorldChampionships)
            )
            .ForMember(
                dest => dest.Status,
                opt => opt.MapFrom(src => 1)
            )
            .ForMember(
                dest => dest.DriverNumber,
                opt => opt.MapFrom(src => src.DriverNumber)
            );
    }
}
```

Now let us update controller to utilise the automapper functionalities

```csharp
private readonly IMapper _mapper;

    public DriversController(
        ILogger<DriversController> logger,
        IMapper mapper)
    {
        _logger = logger;
        _mapper = mapper;
    }

[HttpPost]
    public IActionResult CreateDriver(DriverCreationDto data)
    {
        var _driver = _mapper.Map<Driver>(data);
      
        if(ModelState.IsValid)
        {
            drivers.Add(_driver);

            return CreatedAtAction("GetDriver", new {_driver.Id}, data);
        }

        return new JsonResult("Something went wrong") {StatusCode = 500};
    }
```

We also need to update our program.cs to inject AutoMapper into our DI container 

```csharp
builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
```

Inside Outgoing folder we need add a new class called DriverDto and add the following

```csharp
public class DriverDto
{
    public Guid Id { get; set; }
    public string FullName { get; set; }
    public int DriverNumber { get; set; }
    public int WorldChampionships { get; set; }
}
```

Now let us utilise this DTO by updating our profile class to the following

```csharp
CreateMap<Driver, DriverDto>()
            .ForMember(
                dest => dest.Id,
                opt => opt.MapFrom(src => Guid.NewGuid())
            )
            .ForMember(
                dest => dest.FullName,
                opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}")
            )
            .ForMember(
                dest => dest.DriverNumber,
                opt => opt.MapFrom(src => $"{src.DriverNumber}")
            )
            .ForMember(
                dest => dest.WorldChampionships,
                opt => opt.MapFrom(src => src.WorldChampionships)
            );
```

And now let us update our controller to take advantage to the following

```csharp
[HttpGet]
    public IActionResult GetDrivers()
    {
        var items = drivers.Where(x => x.Status == 1).ToList();

        var driverList = _mapper.Map<IEnumerable<DriverDto>>(items);
        return Ok(driverList);
    }
```

Thank you for reading.