Click here to Skip to main content
15,886,026 members
Articles / Programming Languages / C# 5.0
Tip/Trick

Expressmapper - The New .NET Mapper!

Rate me:
Please Sign up or sign in to vote.
4.94/5 (19 votes)
13 Jul 2015CPOL4 min read 98.1K   29   37
Expressmapper - lightweight, lighting fast and easy to use .NET mapper

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:

Image 1

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:

Image 2

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:

Image 3

Let's imagine if Product and ProductViewModel members' signature is the same:

Image 4

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:

C++
// Expression tree code
.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.

Image 5

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
Passionate back-end, front-end as well as mobile developer on various platforms like .Net, Xamarin, Nodejs and many other frameworks and technologies.

Comments and Discussions

 
NewsUpdated Results - 10/28/2016 Pin
MetalKid00728-Oct-16 3:45
MetalKid00728-Oct-16 3:45 
QuestionMigrate from AutoMapper to ExpressMapper - IOC problem with SimpleInjector initialization Pin
Claudio Barca14-Sep-16 0:03
Claudio Barca14-Sep-16 0:03 
QuestionHow to map from TSource to IEnumerable<TDestination>? Pin
Deilan13-Apr-16 5:11
Deilan13-Apr-16 5:11 
QuestionMapping ViewModel To Model as List<string> to List<onject> Pin
Abolfazl Bahari14-Feb-16 13:28
Abolfazl Bahari14-Feb-16 13:28 
QuestionInheritance Pin
holden1830-Sep-15 11:12
holden1830-Sep-15 11:12 
AnswerRe: Inheritance Pin
Yuriy Anisimov30-Sep-15 11:43
Yuriy Anisimov30-Sep-15 11:43 
GeneralRe: Inheritance Pin
holden1830-Sep-15 11:55
holden1830-Sep-15 11:55 
QuestionPlease help Pin
dibugreen5-Aug-15 4:14
dibugreen5-Aug-15 4:14 
AnswerRe: Please help Pin
Yuriy Anisimov14-Aug-15 4:24
Yuriy Anisimov14-Aug-15 4:24 
QuestionYuriy, it is great mapper! We are close to migrate from AutoMapper to ExpressMapper, but I do not find required feature. Pin
TJSobol17-Jul-15 6:59
TJSobol17-Jul-15 6:59 
AnswerRe: Yuriy, it is great mapper! We are close to migrate from AutoMapper to ExpressMapper, but I do not find required feature. Pin
Yuriy Anisimov17-Jul-15 18:55
Yuriy Anisimov17-Jul-15 18:55 
GeneralImpressive stuff... as long as you don't abuse it Pin
rosdi14-Jul-15 19:00
rosdi14-Jul-15 19:00 
GeneralRe: Impressive stuff... as long as you don't abuse it Pin
Yuriy Anisimov14-Jul-15 21:39
Yuriy Anisimov14-Jul-15 21:39 
GeneralWrong comparison PinPopular
Sergey Morenko14-Jul-15 13:17
professionalSergey Morenko14-Jul-15 13:17 
GeneralRe: Wrong comparison Pin
Yuriy Anisimov14-Jul-15 14:01
Yuriy Anisimov14-Jul-15 14:01 
GeneralRe: Wrong comparison Pin
Sergey Morenko14-Jul-15 14:22
professionalSergey Morenko14-Jul-15 14:22 
GeneralRe: Wrong comparison Pin
Yuriy Anisimov14-Jul-15 14:51
Yuriy Anisimov14-Jul-15 14:51 
GeneralRe: Wrong comparison Pin
Sergey Morenko14-Jul-15 15:07
professionalSergey Morenko14-Jul-15 15:07 
GeneralRe: Wrong comparison Pin
Yuriy Anisimov14-Jul-15 20:53
Yuriy Anisimov14-Jul-15 20:53 
GeneralRe: Wrong comparison PinPopular
Sergey Morenko14-Jul-15 23:18
professionalSergey Morenko14-Jul-15 23:18 
SuggestionAdd ServiceStack to the benchmarks Pin
tig281014-Jul-15 2:37
tig281014-Jul-15 2:37 
GeneralRe: Add ServiceStack to the benchmarks Pin
Yuriy Anisimov14-Jul-15 2:52
Yuriy Anisimov14-Jul-15 2:52 
GeneralRe: Add ServiceStack to the benchmarks Pin
tig281014-Jul-15 3:04
tig281014-Jul-15 3:04 
QuestionDoes it support runtime profile/config ? Pin
spyhunter8814-Jul-15 1:56
spyhunter8814-Jul-15 1:56 
AnswerRe: Does it support runtime profile/config ? Pin
Yuriy Anisimov14-Jul-15 3:20
Yuriy Anisimov14-Jul-15 3:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.