Choosing the Right .NET Core Object Mapper: A Comparative Guide
Object mapping is a fundamental practice in software development, enabling us to convert one type of object to another in a programmatic way. This article aims to compare seven different mapping techniques used in .NET Core: Manual Mapper, Mapperly, Mapster, AutoMapper, TinyMapper, and Implicit Operator.
Comparison Criteria
The comparison will pivot around the following criteria:
- Ease of Use: The learning curve and how quickly a developer can get up and running.
- Flexibility: The ability to handle complex mapping scenarios.
- Performance: The time and computational resources consumed to perform the mapping.
Let’s get started
Create a .Net Core Console Application.
let’s go ahead and create two model classes named Product
and ProductModelView
.
Mapping Strategies and Libraries
Manual Mapper
- Ease of Use: Using manual mappers is often straightforward but can become increasingly complex with complicated objects. No external dependencies.
- Flexibility: Extremely flexible as developers have control over every aspect of the mapping.
- Performance: Usually the fastest since there is no additional computational overhead.
Let’s define the ManualMapper class.
In the ManualMapper class, the Map
function manually transforms a Product
object into a ProductModelView
object. It does this by individually transferring each property from the Product
to its corresponding property in ProductModelView
.
Mapperly
- Ease of Use: Moderate difficulty. Requires initial setup to define mapping rules, and hence, a slight learning curve.
- Flexibility: Highly customizable. Supports conditional mapping and custom conversion functions.
- Performance: Generally high, especially when using simple mappings. Performance can vary with the complexity of the custom logic.
To install the NuGet package.
dotnet add package Riok.Mapperly --version 3.2.0
Let’s define the MapperlyMapper class.
[Mapper(EnumMappingStrategy = EnumMappingStrategy.ByName)]
: This attribute indicates that this is a Mapperly mapping configuration. The EnumMappingStrategy.ByName
specifies that any enum values will be mapped based on their names rather than their numeric values.
public static partial ProductModelView ProductToProductModelView(Product product);
: Declares a static partial method named ProductToProductModelView
. This method takes a Product
object as an argument and returns a ProductModelView
object. The actual mapping logic would be automatically generated by Mapperly based on this method signature and any applied configuration.
Mapster
- Ease of Use: Simple for most scenarios. Offers attribute-based mappings out of the box.
- Flexibility: Excellent. It supports a wide range of mappings, including nested and complex object types.
- Performance: Built for high performance, especially in simple-to-moderate mapping scenarios.
To install the NuGet package.
dotnet add package Mapster --version 7.4.1-pre01
Let’s define the MapsterMapper class.
return TypeAdapter.Adapt<ProductModelView>(product);
: This line does the actual mapping. Mapster's TypeAdapter.Adapt<T>
function is used to convert the given Product
object into a ProductModelView
object.
AutoMapper
- Ease of Use: Moderate. Powerful but requires initial setup and learning.
- Flexibility: Extremely flexible. Can handle complex object graphs, flattening, and other advanced scenarios.
- Performance: Varies. Typically slower than manual methods but acceptable in most scenarios.
To install the NuGet package.
dotnet add package AutoMapper --version 12.0.1
Let’s define the AutoMapper class.
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Product, ProductModelView>();
});
A new MapperConfiguration
object is instantiated.
CreateMap<Product, ProductModelView>()
defines a mapping from Product
to ProductModelView
. AutoMapper will try to map properties with the same name and compatible types automatically.
var mapper = mapperConfig.CreateMapper();
The CreateMapper()
method creates a mapper object based on the earlier configuration. This object will handle the actual data transformation.
return mapper.Map<ProductModelView>(product);
The Map
function takes the Product
object (product
) and returns a new ProductModelView
object with the properties set based on the mapping configuration.
TinyMapper
- Ease of Use: Simple. Designed to be straightforward and easy to set up.
- Flexibility: Limited customization options compared to other libraries, but sufficient for simple mappings.
- Performance: High. Optimized for speed in straightforward mapping scenarios.
To install the NuGet package.
dotnet add package TinyMapper --version 3.0.3
Let’s define the TinyMapper class.
TinyMapper.Bind<Product, ProductModelView>();
: This line sets up the mapping relationship between the Product
and ProductModelView
classes. It tells TinyMapper how to convert an object of type Product
to an object of type ProductModelView
. This is generally done once to configure the mapping rules.
return TinyMapper.Map<ProductModelView>(product);
: This line performs the actual mapping. It takes an object of type Product
as input and returns a new object of type ProductModelView
that has the same values for all the corresponding properties.
Implicit Operator
- Ease of Use: Fairly simple but requires explicit operator definition in code.
- Flexibility: Limited to what the developer manually codes into the operator functions.
- Performance: Generally high as it is almost equivalent to manual mapping.
Let’s define the ImplicitOperatorMapper class.
Here, you’ll notice the simplest method for converting a Product
object to its corresponding ProductModelView
. This is made possible because we've set up an implicit operator inside the Product
model.
Performance and Benchmarks
Let’s create a benchmark class and call it MappersBenchmark
. Inside this class, we'll make a Product
object and name it prod
. To get prod
up and running with some initial values, we'll add a void method named Setup
and do all the assigning in there.
Next, we’ll extend the class by incorporating the mapping functions for the mappers. We’ll decorate it using the Benchmark
attribute to each of these functions to enable the Benchmarking process.
Inside the Program
class, let's fire up BenchmarkRunner
to execute the MappersBenchmark
.
Performance Test Results
From the benchmark scores, it’s clear that Manual mapping takes the lead in speed, followed by Mapperly. The implicit operator also puts up some pretty solid numbers. Considering the pros and cons we’ve discussed for each, along with these performance metrics, you’ve got all you need to pick the mapper that’s the best fit for your project.
Happy Coding!