Intro

ExpressMapper it is lightweight and easy to use .Net mapper to map one type of object(s) to another. ExpressMapper relies completely on the expression trees. Just imagine when you need to map domain model (data entity) to your DTO (viewmodel or data contract) in almost all cases it is a not exciting and boring routing to do. Just take a look at the example when it is done manually.
                            
public ProductViewModel Map(Product p)
{
    var pwm = new ProductViewModel();
    pwm.Id = p.Id;
    pwm.Name = p.Name;
    pwm.Price = p.Price;
    pwm.DiscountedPrice = product.DiscountedPrice;
    pwm.Color = product.Color;
    pwm.Weight = product.Weight;
    pwm.Brand = product.Brand;
    return pwm;
}
                            
                        
                            
// It should be done only once - good practice
// when host app starts e.g. ASP.NET on Application_Start.
public void MappingRegistration()
{
    Mapper.Register<Product,ProductViewModel>();
}

public ProductViewModel GetProduct(Product p)
{
    return p.MapTo<Product,ProductViewModel>();
}
                            
                        
Here you can see when Expressmapper becomes very handy. Once the mappings are registered you can map objects in your code wherever you want either as many times as you want.
Why Expressmapper?
  • It’s lightweight meanwhile it possesses almost all bunch of features
  • It’s lightning fast, take a look at the benchmarks
  • Open source
  • Almost 100% of test coverage
  • Easy to use
                            
// It should be done only once - good practice
// when host app starts e.g. ASP.NET on Application_Start.
public void MappingRegistration()
{
    Mapper.Register<Product,ProductViewModel>();
}

IQueryable<Product> _products;
Product _product;
// Some good examples
public void Examples()
{
    // collection mapping
    var pwms = _products.MapTo<IQueryable<Product>,
            IList<ProductViewModel>>();
    // or just object mapping
    _product.MapTo<Product,ProductViewModel>();
}
                            
                        

Tutorial

Mapping domain model that matches completely DTO. It means that property names and their types are the same.
                            
// Registering mapping
public void RegisterMapping()
{
    // Signature is the same for both type of objects
    Mapper.Register<Product,ProductViewModel>();
}
                            
                        
                            
// Registering mapping
public void RegisterMapping()
{
    // Signature that have different
    // property names but the same meaning
    // Product.Gender is an integer
    // whereas ProductViewModel.Gender is enum
    Mapper.Register<Product,ProductViewModel>()
        .Member(dest => dest.ProductName,
                    src => src.Name)
        .Member(dest => dest.Gender,
                    src => (GenderTypes)src.Gender);
                            
                        
Mapping domain model which is slightly different from DTO is accomplished by “Property” mapping.
Mapping domain model’s property that matches DTO by some complex rule or algorithm is accomplished by “Function” mapping.
                            
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>()
        .Function(dest => dest.Gender,
            src =>
                {
                    if (src.Brand == "Beautiful Bottoms")
                    {
                        return GenderTypes.Women;
                    }
                    switch(src.GenderCode)
                    {
                        case 1:
                        return GenderTypes.Women;
                        case 2:
                        return GenderTypes.Men;
                        default:
                        return GenderTypes.Unisex;
                    }
                });
}
                            
                        
                            
// Registering mapping
public void RegisterMapping()
{
    // Ignore default property mapping use "Ignore"
    Mapper.Register<Product,ProductViewModel>()
                 .Ignore(dest => dest.Discount);
}
                            
                        
To ignore destination default property mapping is accomplished by “Ignore”.
To implement minimum custom mapping like enumeration to string according business rules, etc.. use custom mapping function in order reuse it throughout different mappings' registrations.
                            
public void RegisterMapping()
{
    Mapper.RegisterCustom<ImageSizeTypes, string>(src =>
    {
        switch (src)
        {
            case ImageSizeTypes.ProductSmall:
            return "Small";
            case ImageSizeTypes.ProductMedium:
            return "Medium";
        }
}
                            
                        
                            
public void RegisterMapping()
{
    Mapper
    .RegisterCustom<Unit,UnitViewModel,CustomMapper>();
}
public class CustomMapper:
            ICustomTypeMapper<Unit,UnitViewModel>
{
    public UnitViewModel Map
        (IMappingContext<Unit,UnitViewModel> context)
    {
        return new UnitViewModel{Id = context.Source.Id};
    }
}
                            
                        
To implement custom complex mapping according business rules use custom mapping.
Mapping collection types like: IEnumerable<>, ICollection<>, IList<>, IQueryable<>, typed arrays - no additional steps are involved. Warning: IQueryable<> is implicitly cast to IEnumerable<> - if you need LINQ provide IQueryable support use .Project<Source, destination>();.
                            
public void RegisterMapping()
{
    Mapper.Register<Product, ProductViewModel>();
    Mapper.Register<Category, Category>()
        .Member(dest => dest.Products,
                    src = > src.BrandProducts);
}
// it works just as collection to collection mapping
products.Map<List<Product>,ProductViewModel[]>();
// it works with nested collection's properties too
categories.Map<Category[],List<CategoryViewModel>>();
                            
                        
                            
// Register mappings
public void RegisterMapping()
{
    // Register mapping DTO
    // that has constructor with parameters
    Mapper.Register<Product,ProductViewModel>()
        .Instantiate(src => new ProductViewModel(src.Id));
}
                            
                        
Having not default public constructor or some other logic to instantiate destination DTO - use “Instantiate”.
Need to do pre-mapping initialization or calculations - use “Before” function.
                            
// Register mappings
public void RegisterMapping()
{
    // any initialization logic
    // that needs to take place before mapping
    Mapper.Register<Product,ProductViewModel>()
        .Before((src,dest)=>
                src.Discount = src.Price - src.DiscountedPrice);
}
                            
                        
                            
// Register mappings
public void RegisterMapping()
{
    // any business logic or custom mapping
    // that needs to take place after actual mapping
    Mapper.Register<Product,ProductViewModel>()
        .After((src,dest)=>
            dest.Discount = src.Price - src.DiscountedPrice);
}
                            
                        
Need to do post-mappings or calculations - use “After” function.
Need to reset compiled mapping code’s cache? Use “Reset” function.
                            
// In order to reset compiled mapping cache
// use "Reset" method
Mapper.Reset();
                            
                        
                            
// Register mappings
public void RegisterMapping()
{
    // Any registrations
    Mapper.Register<Product,ProductViewModel>();
    // In order to precompile all registered mappings
    // no compilation during first time mapping
    Mapper.Compile();
}
                            
                        
If there is a need (strongly recommended) to precompile all mappings - use “Compile”.
There is no difference in performance iterating throughout a collection and use “MapTo” method for each item or just mapping collection.
                            
// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
}

public void SomeMethod(Product[] products)
{
    // here is one mapping
    products.MapTo<Product[],List<ProductViewModel>>();
    // here is another way to make the same mapping
    // no performance implication
    var result = new List<ProductViewModel>();
    foreach(var p in products)
    { result.Add(p.MapTo<Product,ProductViewModel>); };
}
                            
                        
                            
// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
}

public void MappingMethod(List<Product> products
                        , ProductViewModel[] productVms)
{
    // here is one mapping
    Mapper.Map<List<Product>
        ,ProductViewModel[]>(products, productsVms);
}
                            
                        
Need to map source to an existing destination without creating new instance(s) of destination. Use .Map<TSrc,TDest>(TSrc source, TDest dest).
Mapping with destination ".Map<TSrc,TDest>(TSrc source, TDest dest)" applies to nested members either a class or a collection types.
                            
public class Product
{
    // Member declarations here
    public ICollection<ProductVariant> Variants{get;set;}
}

public class ProductViewModel
{
    // Member declarations here
    public ICollection<ProductVariantViewModel>
                                        Variants{get;set;}
}

// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
    Mapper.Register<ProductVariant
                        ,ProductVariantViewModel>();
}

public void MappingMethod(List<Product> products,
                            ProductViewModel[] productVms)
{
    // here is one mapping
    Mapper.Map<List<Product>,
    ProductViewModel[]>(products, productsVms);
}
                            
                        
                            
// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
}

public void MappingMethod(object products, object productVms)
{
    // here is mapping without existing destination
    Mapper.Map(products, typeof(List<Product>)
                            ,typeof(ProductViewModel[]));
    // here is mapping with existing destination
    Mapper.Map(products, productsVms,
                            typeof(List<Product>),
                                typeof(ProductViewModel[]));
}
                            
                        
Don't have generic types but objects - you can map just object types with specifying the types. There is non-generics support for existing destination mappings presented too.
Need to map just a constant or value? - Use .Value member configuration.
                            
// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Size, SizeViewModel>()
            .Value(src => src.SortOrder, 123)
            .Value(src => src.BoolValue, true);
}
                            
                        
                            
public void RegisterMapping()
{
    Mapper
        .RegisterCustom<Unit,UnitViewModel
                        ,CustomMapper>();
}

public class CustomMapper:
        ICustomTypeMapper<Unit,UnitViewModel>
{
    public UnitViewModel
        Map(IMappingContext<Unit,UnitViewModel> ctx)
    {
        if (ctx.Destination == default(UnitViewModel))
        {
            ctx.Destination = new UnitViewModel();
        }
        ctx.Destination.Id = ctx.Source.Id;
        return ctx.Destination;
    }
}
                            
                        
Implementation of custom complex mapping with destination.
If there is a custom mapping defined it will be used as the first priority either as a primary mapping or nested one if source and destination match custom mapping types.
                            
public class Product
{
    // Member declarations here
    public List<Unit> Units{get;set;}
}

public class ProductViewModel
{
    // Member declarations here
    public UnitViewModel[] Units{get;set;}
}

public void RegisterMapping()
{
    Mapper
        .RegisterCustom<List<Unit>,UnitViewModel[]
                                        ,CustomMapper>();
    Mapper.Register<Product, ProductViewModel>();
}

public class CustomMapper:
                ICustomTypeMapper<List<Unit>
                                    ,UnitViewModel[]>
    // implementation goes below.....
    public ProductViewModel MappingMethod(Product product)
    {
    // list<Unit> to UnitViewModel[]
    // custom mapping will be used
    return
        Mapper.Map<Product,ProductViewModel>(product);
    }
                            
                        
                            
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
}

public class Product
{
    // Members defined below.
    public decimal Weight{get;set;}
    public int? Height{get;set;}
}
public class ProductViewModel
{
    // Members defined below.
    public decimal? Weight{get;set;}
    public int Height{get;set;}
}
public ProductViewModel Mapping(Product product)
{
    var productVm = Mapper.Map<Product>(product);
    return productVm;
}
                            
                        
Don't worry about mappings like int -> int? or vice versa - Expressmapper handles all conversions automatically.
Don't worry about null checks for nested object type properties - they are handled automatically for you.
                            
// Mapping registration
public void RegisterMapping()
{
    // Null check will be automatically handled in case of
    // "src.DefaultVariant.Size.Code.Name"
    Mapper.Register<Product,ProductViewModel>()
            .Member(dest => dest.Name,
                    src => string.Format({0}-{1},
                    src.Name,
                    src.DefaultVariant.Size.Code.Name));
}
                            
                        

Benchmarks

Extra Small size test (XS)

This test is all about 2 objects with primitive type properties and actually it is not about objects at all. This test all about "Struct" types. Why is it so extra small and simple? - because we are using structs and the same signature for source and destination. Please take a look at the diagram bellow:
Simple struct diagram that is used in test.
Here you will find mapping registration for different mappers:

Expressmapper:

                                
Mapper.Register<Item, ItemViewModel>();
                                
                            

Automapper:

                                
Mapper.CreateMap<Item, ItemViewModel>();
                                
                            

Mapster:

                                
// Enum Mapping is not supported
                                
                            

OoMapper:

                                
// Enum Mapping is not supported
                                
                            

TinyMapper:

                                
// Enum Mapping is not supported
                                
                            

ValueInjecter:

                                
// No code is required for setup mapping...
                                
                            

Small size test (S)

This test is all about 2 objects only with primitive type properties. Why is it so small and simple? - because we are using the same signature for the source and the destination. Please take a look at the diagram bellow:
Simple class diagram
Here you will find mapping registration for different mappers:

Expressmapper:

                                
Mapper.Register<News, NewsViewModel>();
                                
                            

Automapper:

                                
Mapper.CreateMap<News, NewsViewModel>();
                                
                            

Mapster:

                                
// No code is required for setup mapping...
                                
                            

OoMapper:

                                
Mapper.CreateMap<Item, ItemViewModel>();
                                
                            

TinyMapper:

                                
TinyMapper.Bind<Item, ItemViewModel>();
                                
                            

ValueInjecter:

                                
// No code is required for setup mapping...
                                
                            

Medium size test (S)

This test is all about 2 objects with ref type property. Please take a look at the diagram bellow:
Class with ref type property diagram
Here you will find mapping registration for different mappers:

Expressmapper:

                                
Mapper.Register<Role, RoleViewModel>();
Mapper.Register<User, UserViewModel>()
    .Member(dest => dest.BelongTo,
            src => src.Role);
                                
                            

Automapper:

                                
Mapper.CreateMap<Role, RoleViewModel>();
Mapper.CreateMap<User, UserViewModel>()
    .ForMember(dest => dest.BelongTo,
        src => src.MapFrom(m => m.Role));
                                
                            

Mapster:

                                
TypeAdapterConfig<User, UserViewModel>
.NewConfig()
.Map(dest => dest.BelongTo,
    src => TypeAdapter
        .Adapt<Role,
            RoleViewModel<(src.Role));
                                
                            

OoMapper:

                                
Mapper.CreateMap<Role, RoleViewModel>();
Mapper.CreateMap<User, UserViewModel>()
.ForMember(dest => dest.BelongTo,
        src => src.MapFrom(m => m.Role));
                                
                            

TinyMapper:

                                
// Mapping is not supported...
                                
                            

ValueInjecter:

                                
Mapper.AddMap<User, UserViewModel>
                        (src =>
{
    var userViewModel = new UserViewModel();
    userViewModel.InjectFrom(src);
    userViewModel.BelongTo =
        Mapper.Map<Role,
                RoleViewModel>(src.Role);
    return userViewModel;
});
                                
                            

Large size test (L)

This test is all about 2 objects with collection type property. Please take a look at the diagram bellow:
Class with ref collection property type diagram
Here you will find mapping registration for different mappers:

Expressmapper:

                                
Mapper.Register<Article, ArticleViewModel>();
Mapper.Register<Author, AuthorViewModel>()
    .Function(dest => dest.OwnedArticles,
                    src => src.Articles);
                                
                            

Automapper:

                                
Mapper.CreateMap<Article, ArticleViewModel>();
Mapper.CreateMap<Author, AuthorViewModel>()
    .ForMember(dest => dest.OwnedArticles,
        src => src.ResolveUsing(m => m.Articles));
                                
                            

Mapster:

                                
TypeAdapterConfig<Author, AuthorViewModel>
.NewConfig()
.Map(dest => dest.OwnedArticles,
    src =>
        TypeAdapter
            .Adapt<IEnumerable<Article>
                        , ArticleViewModel[]>
                                (src.Articles));
                                
                            

OoMapper:

                                
Mapper.CreateMap<Article, ArticleViewModel>();
Mapper.CreateMap<Author, AuthorViewModel>()
    .ForMember(dest =>
        dest.OwnedArticles,
            src =>
                src.MapFrom(m => m.Articles));
                                
                            

TinyMapper:

                                
// Mapping is not supported
                                
                            

ValueInjecter:

                                
Mapper.AddMap<Author, AuthorViewModel>
        (src =>
{
    var articles =
        new ArticleViewModel[src.Articles.Count()];
    var authorViewModel = new AuthorViewModel();
    authorViewModel.InjectFrom(src);

    for (var i = 0; i < articles.Length; i++)
    {
    articles[i] = Mapper.Map<Article,
                    ArticleViewModel>
                    (src.Articles.ElementAt(i));
    }
    authorViewModel.OwnedArticles = articles;
    return authorViewModel;
});
                                
                            

XL size test (XL)

This test is all about 2 objects with a collection type property as well as nested collections and multiple reference type properties. Please take a look at the diagram bellow:
Complex diagram
Here you will find mapping registration for different mappers:

Expressmapper:

                                
Mapper.Register<ProductVariant,
                ProductVariantViewModel>();
Mapper.Register<Product,
                ProductViewModel>()
    .Member(dest => dest.DefaultSharedOption,
                src => src.DefaultOption);
Mapper.Register<Test, TestViewModel>()
.Member(dest => dest.Age, src => src.Age)
.Member(dest => dest.Weight,
                        src => src.Weight * 2)
.Member(dest => dest.Type,
                src => (Types)src.Type)
.Member(dest => dest.Name,
    src =>
        string.Format("{0} - {1} - {2}",
            src.Name, src.Weight, src.Age))
.Member(dest => dest.Description,
    src =>
        string.Format("{0} - {1}",
                    src.Name, src.Id))
.Member(dest => dest.SpareTheProduct,
            src => src.SpareProduct);
                                
                            

Automapper:

                                
Mapper.CreateMap<ProductVariant,
                    ProductVariantViewModel>();
Mapper.CreateMap<Product,
                    ProductViewModel>()
    .ForMember(dest => dest.DefaultSharedOption,
        src =>
            src.MapFrom(m => m.DefaultOption));
Mapper.CreateMap<Test, TestViewModel>()
.ForMember(dest => dest.Age,
            src => src.MapFrom(src => src.Age))
.ForMember(dest => dest.Weight,
            src =>
                src.MapFrom(src => src.Weight * 2))
.ForMember(dest => dest.Type,
    src =>
        src.MapFrom(m => (Types)m.Type))
.ForMember(dest => dest.Name,
        src => src.MapFrom(m =>
                string.Format("{0} - {1} - {2}",
                    m.Name, m.Weight, m.Age)))
.ForMember(dest => dest.Description,
        src => src.MapFrom(m =>
                string.Format("{0} - {1}",
                        src.Name, src.Id)))
.ForMember(dest => dest.SpareTheProduct,
    src =>
        src.MapFrom(m => m.SpareProduct));
                                
                            

Mapster:

                                
TypeAdapterConfig<Product, ProductViewModel>
.NewConfig()
.Map(dest => dest.DefaultSharedOption,
    src =>
        TypeAdapter.Adapt<ProductVariant,
                    ProductVariantViewModel>
                        (src.DefaultOption));

TypeAdapterConfig<Test, TestViewModel>
.NewConfig()
    .Map(dest => dest.Age, src => src.Age)
    .Map(dest => dest.Weight,
        src => src.Weight * 2)
    .Map(dest => dest.Type,
        src => (Types)src.Type)
    .Map(dest => dest.Name,
        src => string.Format("{0} - {1} - {2}",
                    src.Name, src.Weight, src.Age))
    .Map(dest => dest.Name,
        src => string.Format("{0} - {1} - {2}",
                    src.Name, src.Weight, src.Age))
    .Map(dest => dest.SpareTheProduct,
        src =>
            TypeAdapter.Adapt<Product,
                        ProductViewModel>
                        (src.SpareProduct))
    .Map(dest => dest.Description,
        src => string.Format("{0} - {1}",
                        src.Name, src.Id));
                                
                            

OoMapper:

                                
Mapper.CreateMap<ProductVariant,
                ProductVariantViewModel>();
Mapper.CreateMap<Product,
                ProductViewModel>()
.ForMember(dest => dest.DefaultSharedOption,
    src =>
        src.MapFrom(m => m.DefaultOption));
Mapper.CreateMap<Test, TestViewModel>()
.ForMember(dest => dest.Age,
    src => src.MapFrom(src => src.Age))
.ForMember(dest => dest.Weight,
    src =>
        src.MapFrom(src => src.Weight * 2))
.ForMember(dest => dest.Type,
    src =>
        src.MapFrom(m => (Types)m.Type))
.ForMember(dest => dest.Name,
    src => src.MapFrom(m =>
        string.Format("{0} - {1} - {2}",
            m.Name, m.Weight, m.Age)))
.ForMember(dest => dest.Description,
    src => src.MapFrom(m =>
        string.Format("{0} - {1}",
            src.Name, src.Id)))
.ForMember(dest => dest.SpareTheProduct,
    src =>
        src.MapFrom(m => m.SpareProduct))
.ForMember(dest => dest.Products,
    opt =>
        opt.MapFrom(src => src.Products));
                                
                            

TinyMapper:

                                
// Mapping is not supported
                                
                            

ValueInjecter:

                                
Mapper.AddMap<Product,
    ProductViewModel>(src =>
{
    var productViewModel = new ProductViewModel();
    productViewModel.InjectFrom(src);
    productViewModel.DefaultSharedOption =
    Mapper.Map<ProductVariant,
        ProductVariantViewModel>(src.DefaultOption);
    productViewModel.Options =
            new List<ProductVariantViewModel>();
    foreach (var pv in src.Options)
    {
        productViewModel.Options.Add(
            Mapper.Map<ProductVariant,
                        ProductVariantViewModel>(pv));
    }
    return productViewModel;
});

Mapper.AddMap<Test, TestViewModel>(src =>
{
    var testViewModel = new TestViewModel(
                    string.Format("{0} - {1}",
                            src.Name, src.Id));
    testViewModel.InjectFrom(src);
    testViewModel.Name = string.Format("{0} - {1} - {2}",
                        src.Name, src.Weight, src.Age);

    testViewModel.Product =
            Mapper.Map<Product,
                    ProductViewModel>(src.Product);
    testViewModel.SpareTheProduct =
            Mapper.Map<Product,
                    ProductViewModel>(src.SpareProduct);
    testViewModel.Type = (Types) src.Type;
    testViewModel.Weight = src.Weight*2;
    testViewModel.Products =
            new List<ProductViewModel>();
    foreach (var product in src.Products)
    {
        testViewModel.Products.Add(
            Mapper.Map<Product,
                ProductViewModel>(product));
    }
    return testViewModel;
});
                                
                            

XXL size test (XXL)

This test is all about 2 objects with a collection type property as well as nested collections and multiple reference type properties. Also there are a lot of techniques are used like "Instantiate", "AfterMap", "BeforeMap", etc.. Please take a look at the diagram bellow:
Complex diagram
Here you will find mapping registration for different mappers:

Expressmapper:

                                
Mapper.Register<ProductVariant,
                ProductVariantViewModel>();
Mapper.Register<Product,
                ProductViewModel>()
    .Member(dest => dest.DefaultSharedOption,
            src => src.DefaultOption);
Mapper.Register<Test, TestViewModel>()
.Before((src, dest) => dest.Age = src.Age)
.After((src, dest) => dest.Weight = src.Weight * 2)
.Ignore(dest => dest.Age)
.Member(dest => dest.Type, src => (Types)src.Type)
.Member(dest => dest.Name,
    src =>
    string.Format("{0} - {1} - {2}",
                src.Name, src.Weight, src.Age))
.Function(dest => dest.SpareTheProduct,
                        src => src.SpareProduct)
.Instantiate(src =>
    new TestViewModel(
        string.Format("{0} - {1}",
                        src.Name, src.Id)));
                                
                            

Automapper:

                                
Mapper.CreateMap<ProductVariant,
            ProductVariantViewModel>();
Mapper.CreateMap<Product,
            ProductViewModel>()
.ForMember(dest => dest.DefaultSharedOption,
            src => src.MapFrom(m => m.DefaultOption));
Mapper.CreateMap<Test, TestViewModel>()
.BeforeMap((src, dest) => dest.Age = src.Age)
.AfterMap((src, dest) => dest.Weight = src.Weight * 2)
.ForMember(dest => dest.Age, src => src.Ignore())
.ForMember(dest => dest.Type, src =>
    src.MapFrom(m => (Types)m.Type))
.ForMember(dest => dest.Name,
    src => src.MapFrom(m =>
        string.Format("{0} - {1} - {2}",
                m.Name, m.Weight, m.Age)))
.ForMember(dest => dest.SpareTheProduct,
    src => src.ResolveUsing(m => m.SpareProduct))
.ConstructUsing((src =>
    new TestViewModel(string.Format("{0} - {1}",
                        src.Name, src.Id))));
                                
                            

Mapster:

                                
TypeAdapterConfig<Product, ProductViewModel>
.NewConfig()
.Map(dest => dest.DefaultSharedOption,
        src =>
            TypeAdapter.Adapt<ProductVariant,
                ProductVariantViewModel>
                        (src.DefaultOption));

TypeAdapterConfig<Test, TestViewModel>
.NewConfig()
.Map(dest => dest.Age, src => src.Age)
.Map(dest => dest.Weight,
                src => src.Weight * 2)
.Map(dest => dest.Type,
                src => (Types)src.Type)
.Map(dest => dest.Name,
src => string.Format("{0} - {1} - {2}",
    src.Name, src.Weight, src.Age))
.Map(dest => dest.Name,
    src => string.Format("{0} - {1} - {2}",
            src.Name, src.Weight, src.Age))
.Map(dest => dest.SpareTheProduct,
    src =>
    TypeAdapter.Adapt<Product,
            ProductViewModel>
                (src.SpareProduct))
.Map(dest => dest.Description,
    src => string.Format("{0} - {1}",
                    src.Name, src.Id));
                                
                            

OoMapper:

                                
// BeforeMap, AfterMap,
// Custom constructor are not supported
                                
                            

TinyMapper:

                                
// Mapping is not supported
                                
                            

ValueInjecter:

                                
Mapper.AddMap<Product,
                ProductViewModel>(
                        src =>
{
    var productViewModel = new ProductViewModel();
    productViewModel.InjectFrom(src);
    productViewModel.DefaultSharedOption =
        Mapper.Map<ProductVariant,
            ProductVariantViewModel>(src.DefaultOption);
    productViewModel.Options =
            new List<ProductVariantViewModel>();
    foreach (var pv in src.Options)
    {
        productViewModel.Options.Add(
            Mapper.Map<ProductVariant,
                ProductVariantViewModel>(pv));
    }
    return productViewModel;
});

Mapper.AddMap<Test, TestViewModel>(src =>
{
    var testViewModel = new TestViewModel(
        string.Format("{0} - {1}",
                src.Name, src.Id));
    testViewModel.InjectFrom(src);
    testViewModel.Name = string.Format("{0} - {1} - {2}",
            src.Name, src.Weight, src.Age);

    testViewModel.Product =
            Mapper.Map<Product,
                ProductViewModel>(src.Product);
    testViewModel.SpareTheProduct =
        Mapper.Map<Product,
            ProductViewModel>(src.SpareProduct);
    testViewModel.Type = (Types) src.Type;
    testViewModel.Weight = src.Weight*2;
    testViewModel.Products =
        new List<ProductViewModel>();
    foreach (var product in src.Products)
    {
    testViewModel.Products.Add(
        Mapper.Map<Product,
            ProductViewModel>(product));
    }
    return testViewModel;
});
                                
                            
XS
S
M
L
XL
XXL
  • Axis "X" - quantity of objects being mapped
  • Axis "Y" - milliseconds
  • "-1" value - not supported
All presented tests you can execute by yourself - download solution from the GitHub and run as a start project - "Benchmarks".

If you have some concerns regarding benchmarks, please either send them to support@expressmapper.org or create an issue in the GitHub we'd be grateful for any input.

What's new in Expressmapper 1.8?

                            
// Register mappings
public void RegisterMapping()
{
    // this registration at compilation phase
    // will compile only mapping for source maps
    // e.g. Mapper.Map
    // <Product,ProductViewModel>(product)
    Mapper.Register<Product,ProductViewModel>()
            .CompileTo(CompilationTypes.Source);

    // this registration at compilation phase
    // will compile only mapping for destination maps
    // e.g. Mapper.Map
    // <Ticket,TicketViewModel>(product, productVm)
    Mapper.Register<Ticket,TicketViewModel>()
            .CompileTo(CompilationTypes.Destination);

    // Global default compilation level
    // all mappings will be compiled to Source maps
    // except above mappings as they have major priority
    Mapper.Compile(CompilationTypes.Source);
}
                            
                        
Compilation type management support for "Source" and "Destination" mappings. That kind of switch helps you to avoid unnecessary compilations if you don't use them. It supports globally and at more granular level - per mapping registration. Mapping registration has major priority than global one.
Special support for mapping enum types. It uses integer values from enums as a mapping criteria.
                            
// Just imagine that this enum exists in 2 different namespaces
public enum Types
{
    Source = 2,
    Target = 4
}

public void MappingRegistration
{
    // FlightViewModel.FlightType and Flight.Type
    // both have the same enums' signature
    // but they are located in different namespaces
    Mapper.Register<Flight,FlightViewModel>()
            .Member(dest => dest.FlightType, src => src.Type);
    Mapper.Compile();
}
                            
                        
                            
// Register mappings
public void RegisterMapping()
{
    // all members of that registration
    // will be mapped with case-sensitivity
    Mapper.Register<Product,ProductViewModel>()
        .CaseSensitive(true);

    // by default all members' mappings
    // will ignore case-sensitivity
    // except registration above as
    // local registrations have major priority
    Mapper.MemberCaseSensitiveMap(false);
}
                            
                        
From now on you can manage case-sensitivity at global and per mapping's registration level. Mappping registration level has major priority than global one.
Real IQueryable support for LINQ providers using ".Project<Source, Destination>()". The following restrictions apply almost for every provider:
  • .Function()
  • Using any methods inside custom ".Member" registrations
  • .Before() and .After()
  • .Instantiate()
  • Custom mapping implementations like ICustomTypeMapper<Source,Destination>
  • Others specific per provider...
                            
// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
}

private DbContext _context;

public IQueryable<ProductViewModel> GetProducts()
{
    return
        _context.Set<Product>()
            .Project<Product, ProductViewModel>();
}
                            
                        
                            
// No mappings registrations are needed
public List<ProductViewModel>
            MapProducts(<List<Product> products)
{
    return
        products.Map<List<Product>,
                    List<ProductViewModel>>();
}
                            
                        
Dynamic mapping without registration. Expressmapper would map your "Source" to "Destination" classes like Mapper.Register<Source, Destination>();
Deep copy mappings support. Map source and destination with the same type.
                            
// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,Product>();
}

public Product DeepClone(Product product)
{
    return product.Map<Product,Product>();
}
                            
                        
                            
public class Product
{
    public string Name {set; get;}
}

public class ProductViewModel
{
    public string nAmE {set; get;}
}

// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
}

public ProductViewModel MapProduct(Product product)
{
    return product.Map<Product,ProductViewModel>();
}
                            
                        
Case insensitive support for mapping properties and fields.
Field mapping support.
                            
public class Product
{
    public string Name {set; get;}
}

public class ProductViewModel
{
    public string Name;
}

// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
}

public ProductViewModel MapProduct(Product product)
{
    return product.Map<Product,ProductViewModel>();
}
                            
                        
                            
// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
    Mapper.Compile();
    Mapper.CollectionPrecompile<List<Product>,
                                ProductViewModel[]>()
}

public List<ProductViewModel>
                        MapProducts(<List<Product> products)
{
    return
        products.Map<List<Product>,
            ProductViewModel[]>();
}
                            
                        
Since now you can precompile any collection's mappings apart from registration compilation. Registration compilation compiles only "Source" to "Destination" mapping but not "List<Source>" to "IEnumerable<Destination>" that is specified in you client code and Expressmapper has no idea about it upon registration time. But when it tries to map it in the client code first time, it compiles collection mapping and caches it. With such precompilation improvement you can avoid first compilation in you client code.
Implicit IConvertible conversion mapping support.
                            
public class Product
{
    public int Weight {set; get;}
}

public class ProductViewModel
{
    public long Weight {set; get;}
}

// Register mappings
public void RegisterMapping()
{
    Mapper.Register<Product,ProductViewModel>();
}

public ProductViewModel MapProduct(Product product)
{
    return product.Map<Product,ProductViewModel>();
}