In this article we look at object to object mappers in C# .NET. Mappers solve the common problem of writing mundane and repetitive code when assigning values between objects. Three different mapping libraries are evaluated to give an idea of common themes and some differences which might be relevant for your development needs.
The mapping libraries AutoMapper, AgileMapper and Mapster have been used in a simple demo program to explore some simple mapping scenarios.
If this is a new area for you then hopefully you'll gain some knowledge from reading this article. Or perhaps you have been using the same object mapper for some time; should you consider an alternative?
What is an object mapper and what problem does it solve?
Object to object mapping is the process of assigning values from one object to another. This is common in a layered architectures, particularly those developed using an ORM framework such as Entity Framework Core. There can be many instances in application code where there is a requirement to translate data between the underlying entity model classes and data transfer objects (DTOs). Another case where object mapping is used is between DTOs, typically from a Web API to view model classes in MVC web application.
Mapping code is quite mundane and repetitive to write and a well adopted solution is to use a mapping library. Mapping libraries generally work by matching members in objects and through object graphs by using a set of conventions based on name and in some cases type too.
Benefits of using an Object Mapper
Solving the object to object mapping problem with a mapping library will make your life as a developer easier. Here are some direct benefits:
- Not writing repetitive boring code
- Smaller, cleaner codebase
- Consistent naming of properties across codebase
- It is a strategy which is well adopted by developers
Comparing Object Mapping Libraries
In this post each of the mapping libraries are compared for the following:
- Popularity
- Ease of use
- Ease of configuration
- Collections
- Mapping and reverse mapping
- Flattening and unflattening
- Nested objects
- Projections
- Documentation
For this evaluation, a C# .NET Core 5 console application was used using EntityFramework Core and SqlLite database. It is a simplified model of a layered architecture and available on GitHub.
The project contains a simple service interface which provides a few basic scenarios to test such as a single-level object to object mapping, a two-level object mapping with an object containing a collection and a reverse mapping.
In the project code, the interface is implemented for each mapping solution. Also for good measure, the interface was implemented without using a mapper. This is the mundane code that we wish to remove by using a mapping library!
Mapping can get quite in-depth so not all use cases are covered, but this simple project is available and can be further extended to try out different things.
Object Mapping Libraries
Three object mapping libraries were compared: AutoMapper, AgileMapper and Mapster. There are a range of libraries to use, but here we have a very popular, quite popular and less well-known object mapping library for comparison.
AutoMapper
This is our current object mapper of choice. We decided to use AutoMapper on some recent projects due to its existing popularity and its use in Jon P Smith’s practical book Entity Framework Core in Action.
AutoMapper takes a low-configuration approach and is designed to be used in dependency injection scenarios such as in ASP.NET Core applications.
AutoMapper demands a configuration which can be divided into profiles and validated. The library can perform a scanning operation to include all profiles when the configuration is built. It creates an expression tree which can be inspected during debugging using an extension to view.
AgileMapper
AgileMapper is a zero-configuration, highly-configurable object-object mapper with viewable execution plans.
Members are matched on a set of name and type rules. If the type doesn’t map, no extra config is required to prevent errors.
Execution plans are formed by creating an expression tree and compiling to a function, these are then cached. These plans can be cached upfront and also viewed. The execution plans can be validated during development.
The library also offers deep cloning, updating and merging amongst other useful features.
See https://github.com/agileobjects/AgileMapper
Mapster
Mapster is another zero-configuration object-object mapper that matches by name. Once more, it supports configuration that configuration can be validated.
It uses an extension method which is available on any object to perform the mapping operation. Another extension method provides a convenient way of passing in values at runtime during mapping.
Mapster boasts good performance according to its own GitHub project page and compares itself with other libraries favourably.
The code repo also provides a tool to create DTO classes and extension methods from entity classes which can generate code and save time.
https://github.com/MapsterMapper/Mapster
How Do They Compare
Popularity
In terms of current usage AutoMapper is by far the most popular and established mapping library from the three selected.
|
AgileMapper |
AutoMapper |
Mapster |
NuGet Version |
1.8.0 |
10.1.1 |
7.2.0 |
Total Downloads |
300k |
150M |
2.3M |
Daily Average Downloads |
100 |
39k |
1k |
GitHub Stars |
375 |
8k |
1.5k |
* correct as of 2021-05-17
Ease of use
All three were simple to understand and to setup using NuGet. In terms of usability, Agile Mapper and Mapster were easier than AutoMapper as neither have a requirement for configuration.
Getting started is similar for all three libraries:
- Install package
- Have source and destination types to map
- Create configuration (required for AutoMapper)
- Create instance (required for AutoMapper)
- Perform some mapping!
Agile Mapper and Mapster both offer a static API, something that AutoMapper removed in v9.
Mapster is slightly different to the other two as it provides an extension method for the mapping operation. This is a convenient feature and feels natural to use.
Ease of configuration
Despite working by convention, mapping configuration is required in most cases. A trivial example included in the code example is to map a source first name and last name to a single destination full name field. This was a straightforward exercise in all three using fluent syntax.
All libraries are designed to work with dependency injection with configuration that is created once as a start-up operation. This will provide the application with a compiled set of mappings leading to a performance gain.
Configuration can be easily loaded as all the libraries provide a way of scanning for configuration classes, which is especially useful in larger codebases.
All three support operations before and after mapping takes place. AgileMapper offers some additional extension methods to limit when this should be applied which is a nice feature.
Collections
AgileMapper seems to support more collection types than AutoMapper or Mapster. Both AgileMapper and Mapster also have Dictionary support too, but only for keys with a string type.
During the mapping process, both AutoMapper and AgileMapper initialised empty collections when no value was to be mapped. This behaviour is good practice as collections are expected to be initialised. Mapster didn’t do this and returned nulls as default behaviour.
Mapping and reverse mapping
All three libraries performed straightforward mapping based on matching names. They also performed reverse mapping, although AutoMapper required configuration for this to work.
Another test was to see the default behaviour for a mapping of a larger type into a smaller type, in this case an int to a byte.
All three libraries handled the situation differently.
- AutoMapper threw a runtime exception, so this mapping had to be explicitly ignored.
- AgileMapper ignored the mapping as it has more complex mapping rules.
- Mapster actually performed a mapping and added an incorrect value to the destination member. This is easily preventable using the config to ignore the member.
As with everything, testing and exploring what is actually happening to data and validating configuration is essential.
Flattening
All three support flattening by convention without any special configuration when the destination names followed a simple pattern.
For example: source.Author.StarRating
will map to dest.AuthorStarRating.
Nested objects
All three supported mapping an object graph without any additional configuration. AgileMapper has a dedicated Flattening API with some useful functions such as flattening an object to a dictionary or query string parameter list.
IQueryable Projections
All three support IQueryable projections. This is a convenient way to map an entity object directly to the destination DTO when working with EF Core.
They are all very similar except for AutoMapper which requires the config to be passed into the extension method.
The projection ensured only the required members were declared in the select statement. The code example includes SQL logging so that it possible to inspect the SQL that was generated.
Documentation
Each library has documentation, but AutoMapper and AgileMapper have the clearest and most comprehensive content. Mapster could improve in this area, but hey, this is open source!
A Note on Performance
This study didn’t aim to do a performance comparison. Mapster boasts of superior performance compared to AutoMapper due to its pre-compilation strategy. However, all the libraries work in a similar way using compiled expression trees which are then cached for future execution.
Conclusion
First of all, if you are writing manual object to object mapping code, then start using a mapping library! This will speed up your development leaving you with fewer lines of code to maintain. There is plenty of choice, but any of the three in this article are suitable candidates.
If you are looking for a quick way in, then out of these three AgileMapper gives you a no-fuss, zero-config start and you can start by using its static API.
After looking at AutoMapper, AgileMapper and Mapster, the conclusion is that they are all fairly similar. They all contain the key features required for object mapping tasks. There are subtle usage and configuration differences, but any of them should be fine in most cases.
AutoMapper’s enforced configuration leads to a good habit to define the configuration upfront, but the lack of a static API removes some versatility available in the other two libraries.
Mapster’s extension method approach is a natural way to map objects and their performance figures, if true, are a bonus, but there was an issue in the mapping that needed configuration to resolve.
AgileMapper seems to have a good set of rules for mapping with quality documentation and some useful features not available in the other libraries.
As mentioned previously, in all cases, take the time to validate and check the mapping configuration and be aware that convention-based mapping can lead to data errors in some cases.
These libraries are feature rich and this article has not covered everything they can do. If you want to find out more, then read the docs and feel free to use the demo project!
Thanks for reading this whistle-stop comparison of object mapping libraries.