How to use Hashids with AutoMapper
Tags: dotnet, csharp
Published: 2022-09-01 18:31:18

## Why to use Hashids?
[Hashids](https://github.com/ullmark/hashids.net) helps you to convert a number Id to a string, this can make your app a little more secure hiding the actual id or related key of your DB when the data is requested by an API.
## Why is useful ValueConverter of AutoMapper?
[ValueConverter](https://docs.automapper.org/en/stable/Value-converters.html) can handle the convertion inside te `.Map<TDestination>(source)` method allowing you put the convertion logic in only one place.

## The model
```c#
//Model of your data
public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ClassEnrollmentId { get; set; }
}

//Data Transfer Object (DTO) for your client
public class StudentDto
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string ClassEnrollmentId { get; set; }
}
```
## The configuration
After you install the nuget packages:
- `Install-Package hashids.net`
- `Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection`

Add the following code to the Program.cs to use hashids and AutoMapper with dependency injection, if you prefer to use a class Startup you have to put it on the ConfigureServices method.

```c#
builder.Services.AddSingleton<IHashids>(_ => 
new Hashids("this is my salt"));
// Optional, you can set the length of the string 
// new Hashids("this is my salt", [the number you want])

builder.Services.AddAutoMapper(typeof(AutoMapperProfile));
```
### AutoMapperProfile
```c#
using AutoMapper; // To use the Profile class

public class AutoMapperProfile: Profile
{
	public AutoMapperProfile()
	{
        //This configuration convert the int property who saves Ids to hash string
        CreateMap<Student, StudentDto>()
            .ForMember(d => d.Id, opt => opt.ConvertUsing<HashFormatter, int>())
            .ForMember(d => d.ClassEnrollmentId, opt => opt.ConvertUsing<HashFormatter, int>());


        //This configuration makes the oposite from the above configuration
        CreateMap<StudentDto, Student>()
            .ForMember(d => d.Id, opt => opt.ConvertUsing<IntFromHash, string>())
            .ForMember(d => d.ClassEnrollmentId, opt => opt.ConvertUsing<IntFromHash, string>());
    }
}
```
### Convertion classes

```c#
using AutoMapper; //To use IValueConverter interface
using HashidsNet; //To use IHashids interface

public class HashFormatter : IValueConverter<int, string>
{
    private readonly IHashids hashids;

    public HashFormatter(IHashids hashids)
    {
        this.hashids = hashids;
    }

    public string Convert(int source, ResolutionContext context)
    {
        return hashids.Encode(source);
    }
}
public class IntFromHash : IValueConverter<string, int>
{
    private readonly IHashids hashids;

    public IntFromHash(IHashids hashids)
    {
        this.hashids = hashids;
    }

    public int Convert(string source, ResolutionContext context)
    {
        return hashids.DecodeSingle(source);
    }
}
```
## How to use it
In a controller only have to inject AutoMapper and call the `.Map` method like this.
```c#
public class StudentstController : ControllerBase
{
    private readonly IMapper mapper;

    public StudentstController(IMapper mapper)
    {
        this.mapper = mapper;
    }

    [HttpGet]
    public IActionResult Get()
    {
        //Summaries is the data as List<Student>
        return Ok(mapper.Map<List<StudentDto>>(Summaries));
    }
}
```

## Result

*Data without hash*
```json
[
  {
    "id": 1,
    "name": "Juan",
    "classEnrollmentId": 2
  },
  {
    "id": 2,
    "name": "Pedro",
    "classEnrollmentId": 1
  }
]
```
*Data with hash*
```json
[
  {
    "id": "NV",
    "name": "Juan",
    "classEnrollmentId": "6m"
  },
  {
    "id": "6m",
    "name": "Pedro",
    "classEnrollmentId": "NV"
  }
]
```
Thank you so much for reading this, I hope this be helpful.