Mapster Documentation

A fast, fun and stimulating object to object Mapper

5,094 stars C# C# 1 file ยท ~8,964 tokens #c-sharp#codegenerator#fast#mapper#mapping
RAW Doc

File: README.md

Mapster - The Mapper of Your Domain

Writing mapping methods is a machine job. Do not waste your time, let Mapster do it.

NuGet Sources

NuGet Packages

Package Stable Pre-release
Mapster
Mapster.Core
Mapster.DependencyInjection
Mapster.EFCore
Mapster.EF6
Mapster.JsonNet
Mapster.Immutable
Mapster.Diagnostics
ExpressionDebugger

DotNet Tools

Tool Stable Pre-release
Mapster.Tool

Installation

Install Mapster with the NuGet CLI:

powershell
Install-Package Mapster

Or use the .NET core CLI to install Mapster:

bash
dotnet add package Mapster --project

Basic usage

Mapping to a new object

Mapster creates the destination object and maps values to it.

csharp
var destObject = sourceObject.Adapt<Destination>();

Mapping to an existing object

You create the object, Mapster maps to the object.

csharp
sourceObject.Adapt(destObject);

Use Mapster with Dependency Injection

You can get your IMapper instance via dependency injection, so you do not have to change code, when migrating to mapster from automapper!

Just add Mapster to service collection:

csharp
services.AddMapster();

And use it with DI in your Project:

csharp
public class Test
{
    public Test(IMapper mapper)
    {
        var sourceObject = mapper.Adapt<Destination>();
    }
}

Queryable Extensions

Mapster also provides extensions to map queryables.

csharp
using (MyDbContext context = new MyDbContext())
{
    // Build a Select Expression from DTO
    var destinations = context.Sources.ProjectToType<Destination>().ToList();

    // Versus creating by hand:
    var destinations = context.Sources.Select(c => new Destination {
        Id = c.Id,
        Name = c.Name,
        Surname = c.Surname,
        ....
    })
    .ToList();
}

Generating models & mappers

No need to write your own DTO classes. Mapster provides Mapster.Tool to help you generating models. And if you would like to have explicit mapping, Mapster also generates mapper class for you.

csharp
[AdaptTo("[name]Dto"), GenerateMapper]
public class Student {
    ...
}

Then Mapster will generate for you:

csharp
public class StudentDto {
    ...
}
public static class StudentMapper {
    public static StudentDto AdaptToDto(this Student poco) { ... }
    public static StudentDto AdaptTo(this Student poco, StudentDto dto) { ... }
    public static Expression<Func<Student, StudentDto>> ProjectToDto => ...
}

What's new

Why Mapster?

Performance & Memory efficient

Mapster was designed to be efficient on both speed and memory. The repository includes a benchmark project in src/Benchmark that compares the local Mapster build, its compiler variants, and other modern mapping libraries like AutoMapper, Mapperly, and Facet.

The snapshot below shows the FlatTypes scenario (Person -> PersonDTO), a best-case DTO with simple property-to-property mapping and no nested objects or collections.

[!NOTE]
More complex object shapes can change the relative results. See the complete benchmark results for ComplexTypes, RecursiveTypes, and TotalAllTypes.

Method MapOperations Mean StdDev Error Ns/Map Ratio Gen0 Allocated Alloc Ratio Bytes/Map
Mapster 10.0.8 1000000 6.849 ms 0.6851 ms 1.0358 ms 6.849 1.01 4781 76.29 MB 1.00 80
Mapster 10.0.8 (Roslyn) 1000000 6.579 ms 0.2782 ms 0.4206 ms 6.579 0.97 4781 76.29 MB 1.00 80
Mapster 10.0.8 (FEC) 1000000 6.549 ms 0.9130 ms 1.3803 ms 6.549 0.97 4781 76.29 MB 1.00 80
Mapster 10.0.8 (Codegen) 1000000 5.868 ms 0.3266 ms 0.5488 ms 5.868 0.86 4781 76.29 MB 1.00 80
AutoMapper 14.0.0 1000000 29.645 ms 0.8963 ms 1.5062 ms 29.645 4.37 4750 76.29 MB 1.00 80
Facet 6.5.5 1000000 7.801 ms 1.0231 ms 1.5467 ms 7.801 1.15 8601 137.33 MB 1.80 144
Facet 6.5.5 (Compiled Projection) 1000000 5.508 ms 0.7064 ms 1.0679 ms 5.508 0.81 4781 76.29 MB 1.00 80
Mapperly 4.3.1 1000000 6.521 ms 0.8369 ms 1.2652 ms 6.521 0.96 4781 76.29 MB 1.00 80

Step into debugging

Step-into debugging lets you debug your mapping and inspect values just like your code.

Code Generation

Code generation allows you to

  • Validate mapping at compile time
  • Getting raw performance
  • Seeing your mapping code & debugging
  • Finding usage of your models' properties

There are currently two tools which you can choose based on your preferences.

Change logs

https://github.com/MapsterMapper/Mapster/releases

Usage Guides with Translations

Acknowledgements

JetBrains kindly provides Mapster with a free open-source licence for their Resharper and Rider.

  • Resharper makes Visual Studio a much better IDE
  • Rider is fast & powerful cross platform .NET IDE

File: docs/api/index.md

MapsterMapper Mapster API Documentation

Welcome in the Mapster API documentation!

Use the table of contents to browse API documentation for the MapsterMapper.Mapster Package.


File: docs/api/Reference.md


uid: Mapster.References

References

Basic

Method Description Link
src.Adapt<Dest>() Mapping to new type basic
src.Adapt(dest) Mapping to existing object basic
query.ProjectToType<Dest>() Mapping from queryable basic
Convention & Data type support data types

Mapper instance (for dependency injection)

Method Description Link
IMapper mapper = new Mapper() Create mapper instance mappers
mapper.Map<Dest>(src) Mapping to new type
mapper.Map(src, dest) Mapping to existing object

Builder (for complex mapping)

Method Description Link
src.BuildAdapter() mapper.From(src) Create builder mappers
.ForkConfig(config => ...) Inline configuration config location
.AddParameters(name, value) Passing runtime value setting values
.AdaptToType<Dest>() Mapping to new type
.AdaptTo(dest) Mapping to existing object
.CreateMapExpression<Dest>() Get mapping expression
.CreateMapToTargetExpression<Dest>() Get mapping to existing object expression
.CreateProjectionExpression<Dest>() Get mapping from queryable expression

Config

Method Description Link
TypeAdapterConfig.GlobalSettings Global config config
var config = new TypeAdapterConfig() Create new config instance config instance
src.Adapt<Dest>(config) Passing config to mapping
new Mapper(config) Passing config to mapper instance
src.BuildAdapter(config) Passing config to builder
config.RequireDestinationMemberSource Validate all properties are mapped config validation
config.RequireExplicitMapping Validate all type pairs are defined config validation
config.AllowImplicitDestinationInheritance Use config from destination based class inheritance
config.AllowImplicitSourceInheritance Use config from source based class inheritance
config.SelfContainedCodeGeneration Generate all nested mapping in 1 method TextTemplate
config.Compile() Validate mapping instruction & cache config validation
config.CompileProjection() Validate mapping instruction & cache for queryable
config.Clone() Copy config config instance
config.Fork(forked => ...) Inline configuration config location

Config scanning

Method Description Link
IRegister Interface for config scanning config location
config.Scan(...assemblies) Scan for config in assemblies config location
config.Apply(...registers) Apply registers directly config location

Declare settings

Method Description Link
config.Default Get setting applied to all type pairs config
TypeAdapterConfig<Src, Dest>.NewConfig() config.NewConfig<Src, Dest>() Create setting applied to specific type pairs config
TypeAdapterConfig<Src, Dest>.ForType() config.ForType<Src, Dest>() Get setting applied to specific type pairs config
config.ForType(typeof(GenericPoco<>),typeof(GenericDto<>)) Get setting applied to generic type pairs config
config.When((src, dest, mapType) => ...) Get setting that applied conditionally config
config.ForDestinationType<Dest>() Get setting that applied to specific destination type config
Configuration for nested mapping nested mapping

Settings

Method Description Apply to queryable Link
AddDestinationTransform Clean up data for a specific type x setting values
AfterMapping Add steps after mapping done before-after
AvoidInlineMapping Skip inline process for large type mapping object reference
BeforeMapping Add steps before mapping start before-after
ConstructUsing Define how to create object x constructor
EnableNonPublicMembers Mapping non-public properties non-public
EnumMappingStrategy Choose whether mapping enum by value or by name data types
Fork Add new settings without side effect on main config x nested mapping
GetMemberName Define how to resolve property name x custom naming
Ignore Ignore specific properties x ignore
IgnoreAttribute Ignore specific attributes annotated on properties x attribute
IgnoreIf Ignore conditionally x ignore
IgnoreMember Setup rules to ignore x rule based
IgnoreNonMapped Ignore all properties not defined in Map x ignore
IgnoreNullValues Not map if src property is null shallow & merge
Include Include derived types on mapping inheritance
IncludeAttribute Include specific attributes annotated on properties x attribute
IncludeMember Setup rules to include x rule based
Inherits Copy setting from based type x inheritance
Map Define property pairs x custom mapping
MapToConstructor Mapping to constructor x constructor
MapToTargetWith Define how to map to existing object between type pair custom conversion
MapWith Define how to map between type pair x custom conversion
MaxDepth Limit depth of nested mapping x object reference
NameMatchingStrategy Define how to resolve property's name x custom naming
PreserveReference Tracking reference when mapping object reference
ShallowCopyForSameType Direct assign rather than deep clone if type pairs are the same shallow & merge
TwoWays Define type mapping are 2 ways x 2-ways & unflattening
Unflattening Allow unflatten mapping x 2-ways & unflattening
UseDestinationValue Use existing property object to map data readonly-prop

Attributes

Annotation Description Link
[AdaptMember(name)] Mapping property to different name attribute
[AdaptIgnore(side)] Ignore property from mapping attribute
[UseDestinationValue] Use existing property object to map data attribute
[AdaptTo] [AdaptFrom] [AdaptTwoWays] Add setting on POCO class location
[Mapper] [GenerateMapper] [PropertyType] Define setting for code generation Mapster.Tool

Packages

Packages Method Description
Async setting.AfterMappingAsync builder.AdaptToTypeAsync perform async operation on mapping
Debugging config.Compiler = exp => exp.CompileWithDebugInfo() compile to allow step into debugging
Dependency Injection MapContext.Current.GetService() Inject service into mapping logic
EF 6 & EF Core builder.EntityFromContext Copy data to tracked EF entity
FEC config.Compiler = exp => exp.CompileFast() compile using FastExpressionCompiler
Immutable config.EnableImmutableMapping() mapping to immutable collection
Json.net config.EnableJsonMapping() map json from/to poco and string

Code Generation Tools

Plugin Tool Description
Mapster.Tool dotnet mapster generate DTOs and mapping codes on build
TextTemplate t4 generate mapping codes using t4

File: docs/articles/configuration/Config-for-nested-mapping.md


uid: Mapster.Configuration.NestedMapping title: "Configuration - Config for nested mapping"

For example if you have parent and child classes.

csharp
class ParentPoco
{
    public string Id { get; set; }
    public List<ChildPoco> Children { get; set; }
    public string Name { get; set; }
}

class ChildPoco
{
    public string Id { get; set; }
    public List<GrandChildPoco> GrandChildren { get; set; }
}

class GrandChildPoco
{
    public string Id { get; set; }
}

And if you have setting on parent type.

csharp
TypeAdapterConfig.NewConfig()
    .PreserveReference(true);

By default, children types will not get effect from PreserveReference.

To do so, you must specify all type pairs inside ParentPoco.

csharp
TypeAdapterConfig.NewConfig()
    .PreserveReference(true);
TypeAdapterConfig<ChildPoco, ChildDto>.NewConfig()
    .PreserveReference(true);
TypeAdapterConfig<GrandChildPoco, GrandChildDto>.NewConfig()
    .PreserveReference(true);

Or you can set PreserveReference in global setting.

csharp
TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true);

Fork

You can use Fork command to define config that applies only specified mapping down to nested mapping without polluting global setting.

csharp
TypeAdapterConfig.NewConfig()
    .Fork(config => config.Default.PreserveReference(true));

Ignore if string is null or empty

Another example, Mapster only can ignore null value (IgnoreNullValues), however, you use Fork to ignore null or empty.

csharp
TypeAdapterConfig.NewConfig()
    .Fork(config => config.ForType<string, string>()
        .MapToTargetWith((src, dest) => string.IsNullOrEmpty(src) ? dest : src)
    );

File: docs/articles/configuration/Config-inheritance.md


uid: Mapster.Configuration.Inheritance title: "Configuration - Inheritance"

Implicit inheritance

Type mappings will automatically inherit for source types. Ie. if you set up following config.

csharp
TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig()
    .Map(dest => dest.Name, src => src.Name + "_Suffix");

A derived type of SimplePoco will automatically apply the base mapping config.

csharp
var dest = TypeAdapter.Adapt<DerivedPoco, SimpleDto>(src);
//dest.Name = src.Name + "_Suffix"

If you don't wish for a derived type to use the base mapping, you can turn it off by using AllowImplicitSourceInheritance

csharp
TypeAdapterConfig.GlobalSettings.AllowImplicitSourceInheritance = false;

And by default, Mapster will not inherit destination type mappings. You can turn it on by AllowImplicitDestinationInheritance.

csharp
TypeAdapterConfig.GlobalSettings.AllowImplicitDestinationInheritance = true;

Explicit inheritance

You can copy setting from based type explicitly.

csharp
TypeAdapterConfig<DerivedPoco, DerivedDto>.NewConfig()
        .Inherits<SimplePoco, SimpleDto>();

Include derived types

You can also include derived type to the based type declaration. For example:

csharp
TypeAdapterConfig<Vehicle, VehicleDto>.NewConfig()
    .Include<Car, CarDto>();

Vehicle vehicle = new Car { Id = 1, Name = "Car", Make = "Toyota" };
var dto = vehicle.Adapt<Vehicle, VehicleDto>();

dto.ShouldBeOfType<CarDto>();
((CarDto)dto).Make.ShouldBe("Toyota"); //The 'Make' property doesn't exist in Vehicle

File: docs/articles/configuration/Config-instance.md


uid: Mapster.Configuration.Instance title: "Configuration - Config Instance"

Creating `Config` Instance with `TypeAdapterConfig`

You may wish to have different settings in different scenarios.
If you would not like to apply setting at a static level, Mapster also provides setting instance configurations.

csharp
var config = new TypeAdapterConfig();
config.Default.Ignore("Id");

`ForType` Configuration Methods

For instance configurations, you can use the same NewConfig and ForType methods that are used at the global level with
the same behavior: NewConfig drops any existing configuration and ForType creates or enhances a configuration.

csharp
config.NewConfig<TSource, TDestination>()
        .Map(dest => dest.FullName,
            src => string.Format("{0} {1}", src.FirstName, src.LastName));

config.ForType<TSource, TDestination>()
        .Map(dest => dest.FullName,
            src => string.Format("{0} {1}", src.FirstName, src.LastName));

`Adapt` Configuration Methods

You can apply a specific config instance by passing it to the Adapt method. (NOTE: please reuse your config instance to prevent recompilation)

csharp
var result = src.Adapt(config);

`Map` Configuration Methods

Or to a Mapper instance.

csharp
var mapper = new Mapper(config);
var result = mapper.Map(src);

Cloning `Config` Instance

If you would like to create configuration instance from existing configuration, you can use Clone method. For example, if you would like to clone from global setting.

csharp
var newConfig = TypeAdapterConfig.GlobalSettings.Clone();

Or clone from existing configuration instance

csharp
var newConfig = oldConfig.Clone();

`Fork` Configuration

Fork is similar to Clone, but Fork will allow you to keep configuration and mapping in the same location. See Config Location for more info.

csharp
var forked = mainConfig.Fork(config => 
    config.ForType()
            .Map(dest => dest.code, src => src.Id));

var dto = poco.Adapt<Dto>(forked);

File: docs/articles/configuration/Config-location.md


uid: Mapster.Configuration.Location title: "Configuration - Location"

Entry point for configuration

Configuration should be set only once and reuse for mapping. Therefore, we should not keep configuration and mapping in the same location. For example:

csharp
config.ForType().Ignore("Id");
var dto1 = poco1.Adapt<Dto>(config);

config.ForType().Ignore("Id"); //<--- Exception occurred here, because config was already compiled
var dto2 = poco2.Adapt<Dto>(config);

Therefore, you should separate configuration and mapping. Configuration should keep in entry point such as Main function or Global.asax.cs or Program.cs / Startup.cs.

csharp
// Application_Start in Global.asax.cs
config.ForType().Ignore("Id");
csharp
// in Controller class
var dto1 = poco1.Adapt<Dto>(config);
var dto2 = poco2.Adapt<Dto>(config);

Keep together with mapping

A potential problem with separating configuration and mapping is that the code will be separated into 2 locations. You might remove or alter mapping, and you can forget to update the configuration. Fork method allow you to keep config and mapping inline.

csharp
var dto = poco.Adapt<Dto>(config.Fork(forked => forked.ForType().Ignore("Id"));

Don't worry about performance, forked config will be compiled only once. When mapping occurs for the second time, Fork function will return config from cache.

Using Fork in generic class or method

Fork method uses filename and line number and the key. But if you use Fork method inside generic class or method, you must specify your own key (with all type names) to prevent Fork to return invalid config from different type arguments.

csharp
IQueryable GetItems<TPoco, TDto>()
{
    var forked = config.Fork(
        f => f.ForType<TPoco, TDto>().Ignore("Id"), 
        $"MyKey|{typeof(TPoco).FullName}|{typeof(TDto).FullName}");
    return db.Set<TPoco>().ProjectToType(forked);
}

In separated assemblies

It's relatively common to have mapping configurations spread across a number of different assemblies.
Perhaps your domain assembly has some rules to map to domain objects and your web api has some specific rules to map to your api contracts.

Scan method

It can be helpful to allow assemblies to be scanned for these rules so you have some basic method of organizing your rules and not forgetting to have the registration code called. In some cases, it may even be necessary to register the assemblies in a particular order, so that some rules override others. Assembly scanning helps with this.

Assembly scanning is simple, just create any number of IRegister implementations in your assembly, then call Scan from your TypeAdapterConfig class:

csharp
public class MyRegister : IRegister
{
   public void Register(TypeAdapterConfig config)
   {
      config.NewConfig<TSource, TDestination>();

      //OR to create or enhance an existing configuration
      config.ForType<TSource, TDestination>();
   }
}

To scan and register at the Global level:

csharp
TypeAdapterConfig.GlobalSettings.Scan(assembly1, assembly2, assemblyN)

For a specific config instance:

csharp
var config = new TypeAdapterConfig();
config.Scan(assembly1, assembly2, assemblyN);

Apply method

If you use other assembly scanning library such as MEF, you can easily apply registration with Apply method.

csharp
var registers = container.GetExports();
config.Apply(registers);

Apply method also allows you to selectively pick from one or more IRegister rather than every IRegister in assembly.

csharp
var register = new MockingRegister();
config.Apply(register);

Attributes

You can also set config together with your POCO classes. For example:

csharp
[AdaptTo(typeof(StudentDto), PreserveReference = true)]
public class Student {
    ...
}

File: docs/articles/configuration/Config-validation-and-compilation.md


uid: Mapster.Configuration.ValidationAndCompilation title: "Configuration - Validation and Compilation"

To validate your mapping in unit tests and in order to help with "Fail Fast" situations, the following strict mapping modes have been added.

Explicit Mapping

Forcing all classes to be explicitly mapped:

csharp
//Default is "false"
TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true;
//This means you have to have an explicit configuration for each class, even if it's just:
TypeAdapterConfig.NewConfig();

Checking Destination Member

Forcing all destination properties to have a corresponding source member or explicit mapping/ignore:

csharp
//Default is "false"
TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true;

Validating Mappings

Both a specific TypeAdapterConfig or all current configurations can be validated. In addition, if Explicit Mappings (above) are enabled, it will also include errors for classes that are not registered at all with the mapper.

csharp
//Validate a specific config
var config = TypeAdapterConfig.NewConfig();
config.Compile();

//Validate globally
TypeAdapterConfig.NewConfig();
TypeAdapterConfig.NewConfig();
TypeAdapterConfig.GlobalSettings.Compile();

Config Compilation

Mapster will automatically compile mapping for first time usage.

csharp
var result = poco.Adapt<Dto>();

However, you can explicitly compile mapping by Compile method.

csharp
//Global config
TypeAdapterConfig.GlobalSettings.Compile();

//Config instance
var config = new TypeAdapterConfig();
config.Compile();

Calling Compile method on start up helps you validate mapping and detect problem on start-up time, not on run-time.

NOTE: After compile, when you change setting in config, it will generate errors. Therefore, make sure you finish configuration before calling Compile.


File: docs/articles/configuration/Configuration.md


uid: Mapster.Configuration.Overview title: "Configuration - Overview"

Setting per type pair

You can easily create settings for a type mapping by using: TypeAdapterConfig<TSource, TDestination>.NewConfig().
When NewConfig is called, any previous configuration for this particular TSource => TDestination mapping is dropped.

csharp
TypeAdapterConfig<TSource, TDestination>
    .NewConfig()
    .Ignore(dest => dest.Age)
    .Map(dest => dest.FullName,
          src => string.Format("{0} {1}", src.FirstName, src.LastName));

As an alternative to NewConfig, you can use ForType in the same way:

csharp
TypeAdapterConfig<TSource, TDestination>
    .ForType()
    .Ignore(dest => dest.Age)
    .Map(dest => dest.FullName,
         src => string.Format("{0} {1}", src.FirstName, src.LastName));

ForType differs in that it will create a new mapping if one doesn't exist, but if the specified TSource => TDestination
mapping does already exist, it will enhance the existing mapping instead of dropping and replacing it.

Global setting

Use global settings to apply policies to all mappings.

csharp
TypeAdapterConfig.GlobalSettings.Default.PreserveReference(true);

Then for individual type mappings, you can easily override the global setting(s).

csharp
TypeAdapterConfig<SimplePoco, SimpleDto>.NewConfig().PreserveReference(false);

Rule based settings

To set the setting at a more granular level. You can use the When method in global settings.
In the example below, when any source type and destination type are the same, we will not the copy the Id property.

csharp
TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => srcType == destType)
                               .Ignore("Id");

In this example, the config would only apply to Query Expressions (projections).

csharp
TypeAdapterConfig.GlobalSettings.When((srcType, destType, mapType) => mapType == MapType.Projection)
                                .IgnoreAttribute(typeof(NotMapAttribute));

Destination type only

A setting can also be created without knowing the source type, by using ForDestinationType. For example, you can do AfterMapping setting to validate after mapping.

csharp
TypeAdapterConfig.GlobalSettings.ForDestinationType()
                                .AfterMapping(dest => dest.Validate());

[!NOTE]
ForDestinationType above will always apply to all types assignable to IValidator. If destination class implements IValidator, it will also apply the AfterMapping config.

Open generics

If the mapping type is generic, you can create a setting by passing generic type definition to ForType.

csharp
TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))
                                .Map("value", "Value");

[!Note]
Starting with Mapster version 10.0.8, validation for type pairs settings containing open generic type is disabled.
If you require validation of such settings, you must register the configuration for at least one pair of closed generic types:

csharp
TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; // check all destination member mapping
TypeAdapterConfig.GlobalSettings.ForType(typeof(GenericPoco<>), typeof(GenericDto<>))
                               .Map("value", "Value");

TypeAdapterConfig<GenericPoco, GenericDto<string>>.NewConfig();
TypeAdapterConfig.GlobalSettings.Compile(); // validate

File: docs/articles/mapping/data-types/Collections.md


uid: Mapster.Mapping.DataTypes.Collections title: "Mapping - Collections"

Collections

This includes mapping among lists, arrays, collections, dictionary including various interfaces: IList<T>, ICollection<T>, IEnumerable<T>, ISet<T>, IDictionary<TKey, TValue> etc...

csharp
var list = db.Pocos.ToList();
var target = list.Adapt>();