We just covered different ways to implement object mapping, but here we leverage an open-source tool named AutoMapper that does it for us instead of implementing our own.Why bother learning all of that if a tool already does it? There are a few reasons to do so:
- It is important to understand the concepts; you don’t always need a full-fledged tool like AutoMapper.
- It allowed us to cover multiple patterns we can use in other contexts and apply them to components with different responsibilities. So, all in all, you should have learned multiple new techniques during this object mapping progression.
- Lastly, we dug deeper into applying the SOLID principles to write better programs.
The AutoMapper project is also a copy of the Clean Architecture sample. The biggest difference between this project and the others is that we don’t need to define any interface because AutoMapper exposes an IMapper interface with all the methods we need and more.To install AutoMapper, you can install the AutoMapper NuGet package using the CLI (dotnet add package AutoMapper), Visual Studio’s NuGet package manager, or by updating your .csproj manually.The best way to define our mappers is by using AutoMapper’s profile mechanism. A profile is a simple class that inherits from AutoMapper.Profile and contains maps from one object to another. We use profiles to group mappers together, but in our case, with only three maps, I decided to create a single WebProfile class.Finally, instead of manually registering our profiles, we can scan one or more assemblies to load all of the profiles into AutoMapper by using the AutoMapper.Extensions.Microsoft.DependencyInjection package.
When installing the AutoMapper.Extensions.Microsoft.DependencyInjection package you don’t have to load the AutoMapper package.
There is more to AutoMapper than what we cover here, but it has enough resources online, including the official documentation, to help you dig deeper into the tool. The goal of this project is to do basic object mapping.In the Web project, we must create the following maps:
- Map Product to ProductDetails
- Map NotEnoughStockException to NotEnoughStock
- Map ProductNotFoundException to ProductNotFound
To do that, we create the following WebProfile class (in the Program.cs file, but could live anywhere):
using AutoMapper;
public class WebProfile : Profile
{
public WebProfile()
{
CreateMap<Product, ProductDetails>();
CreateMap<NotEnoughStockException, NotEnoughStock>();
CreateMap<ProductNotFoundException, ProductNotFound>();
}
}
A profile in AutoMapper is nothing more than a class where you create maps in the constructor. The Profile class adds the required methods for you to do that, such as the CreateMap method. What does that do?Invoking the method CreateMap<Product, ProductDetails>() tells AutoMapper to register a mapper that maps Product to ProductDetails. The other two CreateMap calls are doing the same for the other two maps. That’s all we need for now because AutoMapper maps properties using conventions, and both our model and DTO classes have the same sets of properties with the same names.
In the preceding examples, we defined some mappers in the Core layer. In this example, we rely on a library, so it is even more important to consider the dependency flow. We are mapping objects only in the Web layer, so there is no need to put the dependency on AutoMapper in the Core layer. Remember that all layers depend directly or indirectly on Core, so having a dependency on AutoMapper in that layer means all layers would also depend on it.
Therefore, in this example, we created the WebProfile class in the Web layer instead, limiting the dependency on AutoMapper to only that layer. Having only the Web layer depend on AutoMapper allows all outer layers (if we were to add more) to control how they do object mapping, giving more independence to each layer. It is also a best practice to limit object mapping as much as possible.
I’ve added a link to AutoMapper Usage Guidelines in the Further reading section at the end of the chapter.
Now that we have one profile, we need to register it with the IoC container, but we don’t have to do this by hand; we can scan for profiles from the composition root by using one of the AddAutoMapper extension methods to scan one or more assemblies:
builder.Services.AddAutoMapper(typeof(WebProfile).Assembly);
The preceding method accepts a params Assembly[] assemblies argument, meaning we can pass multiple Assembly instances to it.
That AddAutoMapper extension method comes from the AutoMapper.Extensions.Microsoft.DependencyInjection package.
Leave a Reply