"Mastering AutoMapper: Simplifying Object Mapping in .NET"
Tags: dotnet, csharp, sql, webdev
Published: 2023-09-18 06:55:11

Hello folks today we will be doing a walk-through on how to use AutoMapper in a dotnet web application, and its benefit over traditional mapping.

**Ingredients**
Dotnet Core SDK

Prerequisite
🏝️ Basic Knowledge of C#
🏝️ How to use the terminal

Agenda
🪢 Introduction: what is Automapper
🪢 Domain models and our view models
🪢 Create a new C# project in Rider.
🪢 Manual Mapping in Action
🪢 Automapper in Action


##### What is an AutoMapper
AutoMapper _is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another. [reference](https://automapper.org/)_

Essentially, an AutoMapper helps to map our `domain models` to our `view models`. We can use AutoMapper in our controller or in a separate helper class.


#### Domain models and our view models
- Domain models are the models that represent the data in our database.
- View models are the models that represent the data that we want to show to the user. e.g the data transfer objects (DTOs) that we use in our API.

##### Example
The `User.cs` class below is the internal representation of the data in our database. 
```csharp
public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public DateTime DateOfBirth { get; set; }
    public int Age { get; set; }
    public string KnownAs { get; set; }
    public DateTime Created { get; set; } = DateTime.Now;
    public DateTime LastActive { get; set; } = DateTime.Now;

}
```


##### View models example
Next we have the `UserForListDto.cs` class below. This is the model that we want to send to the client. It contains only the properties that we want to send to the client.
```csharp
public class UserForListDto
{
    public int Id { get; set; }
    public string Username { get; set; }
    public int Age { get; set; }
    public string KnownAs { get; set; }
}
```

##### UserCreateDto.cs
This is the model that we want to receive from the client for creating a new user.
```csharp
public class UserCreateDto
{
    public string Username { get; set; }
    public string Password { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string KnownAs { get; set; }
    public int Age { get; set; }
}
```

Let's create a new Dotnet Core API project to demonstrate how to map our domain models to our view models manually.

##### Create a new Dotnet Core API project
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7znl4q0pe92nhrzc6f6w.png)

-o is the output directory that we want to create the project in.


Open the project in your favorite code editor. I am using JetBrains Rider.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xnybjw3j3i1fgxff3qxh.png)

##### Create a new folder called Models
```bash
mkdir Models
```

Then create the User.cs in the Models folder.
```bash
touch Models/User.cs
```

##### User.cs
```csharp
public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public DateTime DateOfBirth { get; set; }
    public int Age { get; set; }
    public string KnownAs { get; set; }
    public DateTime Created { get; set; } = DateTime.Now;

}
```

##### Create a new folder called Dtos
```bash
mkdir Dtos
```

Then create the UserResponse.cs in the Dtos folder.
```bash
touch Dtos/UserResponse.cs
```

##### UserResponse.cs
For Read operations, we want to send the UserResponse model to the client. This model contains only the properties that we want to send to the client.
```csharp
public class UserResponse
{
   public int Id { get; set; }
    public string Username { get; set; } = String.Empty;
    public int Age { get; set; }
    public string KnownAs { get; set; } = String.Empty;
}
```

##### Dtos/CreateUserDto.cs
For Create operations, we want to receive the CreateUserDto model from the client. This model contains only the properties that we want to receive from the client.
```csharp
public class CreateUserDto
{
    public string Username { get; set; } = String.Empty;
    public string Password { get; set; } = String.Empty;
    public DateTime DateOfBirth { get; set; }
    public string KnownAs { get; set; } = String.Empty;
    public int Age { get; set; }
}
```

##### Create a new folder called Data
```bash
mkdir Data
```

Then create the DataContext.cs in the Data folder.
```bash
touch Data/DataContext.cs
```

##### DataContext.cs
DataContext.cs bridges the gap between our application and the database.

```csharp
public class DataContext : DbContext
{
    public DataContext(DbContextOptions<DataContext> options) : base(options)
    {
    }

    public DbSet<User> Users { get; set; }
}
```

##### Let's install the packages that we need
```bash
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
```

Check the `.csproj` file to ensure the packages were installed

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lie5kkax4scpw8sr9wve.png)


Next we can import the missing references

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k07jo3iwy3efufedqkyd.png)

##### Create a new folder called Repositories
```bash
mkdir Repositories
```

Then create the IAuthRepository.cs in the Repositories folder.
```bash
touch Repositories/IAuthRepository.cs
```

##### IAuthRepository.cs
```csharp
public interface IAuthRepository
{
    Task<UserResponse> Register(CreateUserDto user);
}
```

##### Create a new folder called Repositories
```bash
mkdir Repositories
```

Then create the AuthRepository.cs in the Repositories folder.
```bash
touch Repositories/AuthRepository.cs
```



##### Mapping without AutoMapper
```csharp

public class AuthRepository: IAuthRepository
{
    private readonly DataContext _context;

    public AuthRepository(DataContext context)
    {
        _context = context;
    }

    public async Task<UserResponse> Register(CreateUserDto user)
    {
        // insert the user into the database from the CreateUserDto
        var userToCreate = new User
        {
            Username = user.Username,
            DateOfBirth = user.DateOfBirth,
            KnownAs = user.KnownAs,
            Age = user.Age
        };

        // save the user to the database
        var createdUser = await _context.Users.AddAsync(userToCreate);
        // save the changes to the database
        await _context.SaveChangesAsync();

        // return the user to the client in the UserResponse format
        return new UserResponse
        {
            Id = createdUser.Entity.Id,
            Username = createdUser.Entity.Username,
            Age = createdUser.Entity.Age,
            KnownAs = createdUser.Entity.KnownAs
        };
    }
}
```

##### Let's the AuthController.cs
```bash
touch Controllers/AuthController.cs
```

##### AuthController.cs
```csharp
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
    private readonly IAuthRepository _repo;

    public AuthController(IAuthRepository repo)
    {
        _repo = repo;
    }

    [HttpPost("register")]
    public async Task<IActionResult> Register(CreateUserDto user)
    {
        var createdUser = await _repo.Register(user);

        return Ok(createdUser);
    }
}
```

##### appsettings.json
Let's add our connection string to the appsettings.json file.
##### appsettings.json
Let's add our connection string to the appsettings.json file.
```json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost,1433;Database=bookstoredb;User Id=SA;Password=YourPassword;Encrypt=false;TrustServerCertificate=True;"
  }
}

```


##### Program.cs
Next, let's add our connection string to the Program.cs file and add the DataContext to the DI container.
```csharp
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");

// DI for DataContext
builder.Services.AddDbContext<DataContext>(options =>
    options.UseSqlServer(connectionString));

// DI for Interfaces and Implementations
builder.Services.AddScoped<IAuthRepository, AuthRepository>();

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
```

##### Run migrations
```bash
dotnet ef migrations add InitialCreate
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxpsh52rv4pylowxqf67.png)

```bash
dotnet ef database update
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/djha0n7z0yxvxgbhpc4t.png)




##### Run the project
```bash
dotnet run
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfor44agwe0zicpwyd78.png)

##### open the swagger ui
http://localhost:5108/swagger/index.html
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sl9bl1qwpoial4x1cjdm.png)

We only exposed the data we want to the client to see coming from the view model called `CreateUserDto` we created earlier.

Let fill our payload

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yejt1qfhvhi5c2zo6t35.png)


We get a `200` Ok Response and Only exposed the Data coming from our `UserResponse.cs` class
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/16vzp8s5i2r628hhude1.png) 



##### Mapping with AutoMapper
Let's create a helper class called MappingProfile.cs in the Helpers folder.
```bash
touch Helpers/MappingProfile.cs
```

##### MappingProfile.cs
```csharp
public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<User, UserResponse>();
        CreateMap<CreateUserDto, User>();
    }
}
```

###### Let's explain the code above. 
- The `CreateMap<User, UserResponse>();` maps the User model (the source) to the UserResponse model (the destination). i.e we want to convert the User model to the UserResponse model before we send it to the client.
- The `CreateMap<CreateUserDto, User>();` maps the CreateUserDto model (the source) to the User model (the destination). i.e we want to convert the CreateUserDto model to the User model before we save it to the database.

##### Let's update the AuthRepository.cs
```csharp
public class AuthRepository : IAuthRepository
{
    private readonly DataContext _context;
    private readonly IMapper _mapper;

    public AuthRepository(DataContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }

    public async Task<UserResponse> Register(CreateUserDto user)
    {
        // insert the user into the database from the CreateUserDto
        var userToCreate = _mapper.Map<User>(user); // map the CreateUserDto(user) to the User model in the <User> format

        // save the user to the database
        var createdUser = await _context.Users.AddAsync(userToCreate);
        // save the changes to the database
        await _context.SaveChangesAsync();

        // return the user to the client in the UserResponse format
        return _mapper.Map<UserResponse>(createdUser.Entity);
    }
}
```

NOTE: We don't need to update the AuthController.cs because we are using the DI container to inject the IMapper into the AuthRepository.cs

##### Let's configure AutoMapper in the DI container
```csharp
var builder = WebApplication.CreateBuilder(args);
//..

// DI for AutoMapper
builder.Services.AddAutoMapper(typeof(MappingProfile));

//..
```

##### Run the project
```bash
dotnet run
```

##### open the swagger ui
http://localhost:5108/swagger/index.html

We got the same result, but this time with fewer lines and more organised code

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fyt5ms9gxgi7qby73s9y.png)



##### Egde cases
- What if we want to map a property to a different property name?
- What if we want to map a property to a different property type?

Let's assume the UserResponse model has a property called `ProfilePictureUrl` and the User model has a property called `PhotoUrl`. We want to map the `PhotoUrl` property to the `ProfilePictureUrl` property.

##### UserResponse.cs
```csharp
public class UserResponse
{
    public int Id { get; set; }
    public string Username { get; set; } = String.Empty;
    public int Age { get; set; }
    public string KnownAs { get; set; } = String.Empty;
    public string ProfilePictureUrl { get; set; } = String.Empty;
}
```


##### User.cs
```csharp
public class User
{
    public int Id { get; set; }
    public string Username { get; set; } = String.Empty;
    public string Password { get; set; } = String.Empty;
    public DateTime DateOfBirth { get; set; }
    public int Age { get; set; }
    public string KnownAs { get; set; } = String.Empty;
    public DateTime Created { get; set; } = DateTime.Now;
    public string PhotoUrl { get; set; } = String.Empty;
}
```

Note: Ensure you run the migrations and update the database before you continue.

##### MappingProfile.cs
```csharp
public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<User, UserResponse>()
            .ForMember(dest => dest.ProfilePictureUrl, opt =>
                opt.MapFrom(src => src.PhotoUrl));
        CreateMap<CreateUserDto, User>();
    }
}
```
Explanation: We are mapping the `PhotoUrl` property to the `ProfilePictureUrl` property using the `ForMember` method.
Basically, we are saying map the `PhotoUrl` property from the source(`User`) to the `ProfilePictureUrl` property in the destination(`UserResponse`



##### Run the project
```bash
dotnet run
```


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s63got7bb40qookagbuy.png)
Notice we have the `PhotoUrl` property in the request body. But we want to map the `PhotoUrl` property to the `ProfilePictureUrl` property in the UserResponse model.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vcf1m7rjnsxj67wkvm1s.png)


##### Conclusion
In this article, we have seen how to map our domain models to our view models using AutoMapper. We have also seen how to map a property to a different property name and how to map a property to a different property type. I hope you have learned something new. Thanks for reading.