Click here to Skip to main content
15,888,733 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.2K   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

 
GeneralRe: Does it support runtime profile/config ? Pin
spyhunter8814-Jul-15 6:37
spyhunter8814-Jul-15 6:37 
QuestionPerformance Comparison Pin
hollysong14-Jul-15 1:37
hollysong14-Jul-15 1:37 
AnswerRe: Performance Comparison Pin
Yuriy Anisimov14-Jul-15 2:48
Yuriy Anisimov14-Jul-15 2:48 
QuestionDoes it support mapping by convention & nested properties? Pin
Mohammed Elmahdy14-Jul-15 1:23
Mohammed Elmahdy14-Jul-15 1:23 
AnswerRe: Does it support mapping by convention & nested properties? Pin
Yuriy Anisimov14-Jul-15 1:37
Yuriy Anisimov14-Jul-15 1:37 
GeneralRe: Does it support mapping by convention & nested properties? Pin
mesque1club3-Sep-15 4:04
mesque1club3-Sep-15 4:04 
GeneralRe: Does it support mapping by convention & nested properties? Pin
Yuriy Anisimov3-Sep-15 7:01
Yuriy Anisimov3-Sep-15 7:01 
QuestionSupport for mapping by convention & nested properties Pin
Mohammed Elmahdy14-Jul-15 1:22
Mohammed Elmahdy14-Jul-15 1:22 
QuestionIs it something like auto mapper lib Pin
Tridip Bhattacharjee13-Jul-15 22:01
professionalTridip Bhattacharjee13-Jul-15 22:01 
AnswerRe: Is it something like auto mapper lib Pin
Yuriy Anisimov13-Jul-15 22:11
Yuriy Anisimov13-Jul-15 22:11 
AnswerRe: Is it something like auto mapper lib Pin
wout de zeeuw13-Jul-15 22:39
wout de zeeuw13-Jul-15 22:39 

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.