Introduction
Expressmapper - lightweight, lighting fast and easy to use .NET mapper to map one type of object(s) to another in an automated and easy way.
Background
Nowadays, there are a lot of layers in every good-architectured solution like Data Access Layer (DAL), Business logic layer, Service layer, Front-end layer and many others. And of course, each layer operates with its own objects' types like domain object layer or "Data entities", DTO or business objects, data contracts, view models etc... These objects are the same logically and have the same data, but sometimes they have different signature of members due to the context of usage by specific layer. Let's illustrate some examples:
From the image above, there is a Product
data entity and ProductViewModel
, view model which is displayed e.g. on the web page. Let's assume that specification states that there is a need to display only the final price on the page without initial price nor discount as well as there is a need only in brand name and names of the sizes. Let's illustrate the code that is used in order to map Product
to ProductViewModel
:
Try to imagine if you have a production project which has 100 - 1000 mappings like the above or imagine that 70% of your data entities and view models have the same member signature. So you have to write a lot of code which is very boring and cumbersome. And the main point - it takes an enourmous amount of time to write it! Here Expressmapper comes to the rescue and it keeps you away from that kind of nightmare maintenance. Just take a look at the image below to see how it is done in Expressmapper way:
Let's imagine if Product
and ProductViewModel
members' signature is the same:
Just one line of code impressive huh?
Actually, there are a lot of other solutions out there for mapping one object type to another but the question is what to choose and what is the best? There are three main criterias:
- Performance
- Features
- Quality and support
Performance
One of the main points why Expressmapper has been created - the ultimate performance. Expressmapper relies completely on the expression trees which means it's real precomiled .NET code - it's the same as you write the code, compile it and then execute it - no reflection has been used. Let's take a look at the expression trees' code how Product
to ProductViewModel
mapping looks like:
.Lambda #Lambda1<System.Func`2[Product,ProductViewModel]>(Product $src)
{
.Block(ProductViewModel $dest) {
$dest = .New ProductViewModel();
$dest.FinalPrice = $src.Price - $src.Discount;
.If ($src.Brand == .Default(Brand)) {
$dest.BrandName = .Default(System.String)
} .Else {
$dest.BrandName = ($src.Brand).Name
};
$dest.Id = $src.Id;
$dest.Name = $src.Name;
$dest.Weight = $src.Weight;
$dest.Description = $src.Description;
$dest.Color = $src.Color;
.If ($src.Sizes == null) {
$dest.Sizes = .Default(System.Collections.Generic.List`1[System.String])
} .Else {
.Block() {
.If ($src.Sizes == null) {
$dest.Sizes = .Default(System.Collections.Generic.List`1[System.String])
} .Else {
.Block(
System.Collections.Generic.List`1[Size] $sizes,
System.Collections.Generic.List`1[System.String] $sizesVm,
System.Collections.Generic.IEnumerator`1[Size] $sizesEnum) {
$sizes = $src.Sizes;
$sizesVm = .New System.Collections.Generic.List`1[System.String]();
$sizesEnum = .Call $sizes.GetEnumerator();
.Loop {
.If (.Call $sizesEnum.MoveNext() != False) {
.Block(
Size $currentSize,
System.String $sizeStr) {
$currentSize = $sizesEnum.Current;
.Block() {
.If ($currentSize == .Default(Size)) {
$sizeStr = .Default(System.String)
} .Else {
.Block(
System.String $sizeStr) {
.Block() {
.Block(
ExpressMapper.ICustomTypeMapper`2
[Size,System.String] $var1,
ExpressMapper.DefaultMappingContext`2
[Size,System.String]
$context5f2a2f7d-11b8-4355-bfa8-2e0d65b474a0)
{
$var1 = (ExpressMapper.ICustomTypeMapper`2
[Size,System.String]).Constant
<ExpressMapper.ICustomTypeMapper>
(ExpressMapper.DelegateCustomTypeMapper`
2[Size,System.String]);
$context5f2a2f7d-11b8-4355-bfa8-2e0d65b474a0 =
.New ExpressMapper.DefaultMappingContext`
2[Size,System.String]()
;
$context5f2a2f7d-11b8-4355-bfa8-2e0d65b474a0.
Source = $currentSize;
$sizeStr = .Call $var1.Map
($context5f2a2f7d-11b8-4355-bfa8-2e0d65b474a0)
}
};
$sizeStr = $sizeStr
}
}
};
.Call $sizesVm.Add($sizeStr)
}
} .Else {
.Break #Label1 { }
}
}
.LabelTarget #Label1:;
$dest.Sizes = $sizesVm
}
}
}
};
$dest
}
}
It's almost the same code as in the first image when we tried manually to write mapping from Product
to ProductViewModel
plus some checks. This expression code will be compiled into a delegate and saved into the mappings' cache. So when you call the delegate - it's completely the same performance as hand-written code with all JIT optimizations.
Here you can take a look at the comparison performace chart for some complex mapping case for more details, please click here:
Versions of mappers that have been used:
Expressmapper | 0.9.5.0 |
Automapper | 3.3.1.0 |
Mapster | 1.14.0.0 |
ValueInjector | 3.0.1.0 |
OoMapper | 0.2.0.33 |
TinyMapper | 1.0.3.19 |
"-1" - Not supported means that it still could be done by "Custom Mapping" e.g. using TypeConverter
(Wrapped hand-written code) but any hand-written code is out of scope of the benchmarks.
You can see that Expressmapper outruns hand-written code - but it is actually the same as it depends on how JIT optimization works.
Features
Another very important aspect what kind of features mapper supports like mapping with existing destination "Map<TSrc,TDest>(TSrc src, TDest dest)
", non-generics mapping, custom mappings, null
reference checks, nested null
reference checks, IConvertible
conversion between the types, nullable to non-nullable conversions and vice-versa, etc. While Expressmapper is lightning fast, it supports almost all bunch of features that other mappers do. All the features' explanation can be found here.
Quality and Support
Expressmapper has almost 100% tests coverage and there are a lot of performance benchmarks. And it is being rapidly stuffing with new features as you can see from Github.
Is It Production Ready?
YES! Expressmapper has been integrated in 4 big projects at the current moment that we are aware of. Each project has in average 100 - 300 mapping registrations and diversity of complex mappings.
Summary
Expressmapper team hopes that you will find this open source library as a valuable asset in your projects and really enjoy using it because of the performance, features and easyness. And we'll appreciate any input that can improve Expressmapper.