Mapster Documentation

repository·master·Indexed 26 days ago

https://github.com/mapstermapper/mapster

A high-performance, memory-efficient object-to-object mapper for .NET. Mapster automates data mapping between object models, such as Domain models to DTOs, and features support for code generation, dependency injection, EF Core projections via ProjectToType, and the Builder pattern for complex configurations. It includes specialized packages for Async, EF6, EF Core, Immutable collections, and Json.NET, as well as the Mapster.Tool for build-time mapping code generation.

Tokens
28.9K
Snippets
134
Records
179
Agent score
87%

What's inside Mapster

  1. Identify Mapster benchmark scenarios

    master

    Benchmarks are categorized by the complexity of the object shapes being mapped:

    • FlatTypes: Maps Person -> PersonDTO. A simple property-to-property copy with no nested objects or collections. Highlights mapper call overhead and IL quality.
    • ComplexTypes: Maps Customer -> CustomerDTO. Includes nested address mapping, array/list shape changes, and flattening rules (e.g., AddressCity <- Address.City). Represents typical DTOs with nested objects and collections.
    • RecursiveTypes: Maps Foo -> FooDTO. Uses a self-recursive type where an object can contain itself or collections of itself. Highlights performance in deep mapping graphs.
    • TotalAllTypes: A batch scenario that runs FlatTypes, RecursiveTypes, and ComplexTypes sequentially.
  2. Map to pure readonly properties using `[UseDestinationValue]` attribute

    master

    For properties that are truly readonly (no setter at all, only a getter), you can annotate the property with the [UseDestinationValue] attribute. This tells Mapster to use the existing value of the destination object instead of trying to set it.

    public class Order {
        public string Id { get; set; }
    
        [UseDestinationValue]
        public ICollection<OrderItem> Items { get; } = new List<OrderItem>();
    }
  3. Override property types with [PropertyType]

    master

    Mapster's default behavior is to forward property types to their corresponding DTO types (e.g., ICollection<Enrollment> becomes ICollection<EnrollmentDto>). You can override this behavior using the [PropertyType(typeof(Target))] attribute, which can be applied to either a class or a specific property.

    [AdaptTo("[name]Dto")]
    public class Student {
        public ICollection<Enrollment> Enrollments { get; set; }
    }
    
    [AdaptTo("[name]Dto"), PropertyType(typeof(DataItem))]
    public class Enrollment {
        [PropertyType(typeof(string))]
        public Grade? Grade { get; set; }
    }
  4. Configure two-way mapping with `TwoWays()`

    master

    When you need to map an object from a POCO to a DTO and back from the DTO to the POCO, you can use the .TwoWays() method. This allows you to define a mapping rule once, and Mapster will automatically apply the reverse mapping.

    Important: You must call .TwoWays() before defining specific .Map() rules for them to apply to both directions. Rules defined before calling .TwoWays() will only apply to the initial direction (POCO to DTO).

    // This mapping applies to both directions: dto.Code = poco.Id AND poco.Id = dto.Code
    TypeAdapterConfig<Poco, Dto>
        .NewConfig()
        .TwoWays()
        .Map(dto => dto.Code, poco => poco.Id);
    
    // Example of order importance:
    TypeAdapterConfig<Poco, Dto>
        .NewConfig()
        .Map(dto => dto.Foo, poco => poco.Bar)  // Only applies Poco -> Dto
        .TwoWays()
        .Map(dto => dto.Foz, poco => poco.Baz); // Applies both directions
  5. Generate mapper extension methods

    master

    To generate static extension methods (like AdaptToDto, AdaptTo, and ProjectToDto) for your generated models, you must explicitly call GenerateMapper for each type in your configuration. This works in conjunction with your AdaptFrom, AdaptTo, or AdaptTwoWays declarations.

    config.AdaptTo("[name]Dto")
        .ForType<Student>();
    
    config.GenerateMapper("[name]Mapper")
        .ForType<Student>();
  6. Organize Mapster configuration and mapping locations

    master

    To avoid exceptions caused by re-configuring already compiled configurations, you should separate your configuration logic from your mapping logic.

    • Configuration: Define your rules once at the application entry point (e.g., Main, Global.asax.cs, Program.cs, or Startup.cs).
    • Mapping: Perform the actual mapping (e.g., using .Adapt<T>()) within your business logic or controllers using the pre-configured instance.
    // 1. Configuration (e.g., in Global.asax.cs)
    config.ForType<Poco, Dto>().Ignore("Id");
    
    // 2. Mapping (e.g., in a Controller)
    var dto1 = poco1.Adapt<Dto>(config);
    var dto2 = poco2.Adapt<Dto>(config);
  7. Configure mapping for types containing only non-public members

    master

    If a type contains no public properties, Mapster may treat it as a primitive type. To ensure non-public members are mapped in such cases, you must both enable non-public members globally (or for the pair) and explicitly declare the type pair configuration.

    TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMembers(true);
    TypeAdapterConfig<PrivatePoco, PrivateDto>.NewConfig();
  8. Pass run-time values to mappings

    master

    To use run-time data (like the current user's name) during a mapping operation, follow these two steps:

    1. Configure the mapping to retrieve the value from MapContext.Current.Parameters using a specific key.
    2. Execute the mapping using BuildAdapter() and provide the value via AddParameters(key, value).

    This allows you to inject dynamic data that is not present in the source object itself.

    // 1. Configuration
    TypeAdapterConfig<Poco, Dto>.NewConfig()
                                .Map(dest => dest.CreatedBy,
                                     src => MapContext.Current.Parameters["user"]);
    
    // 2. Execution
    var dto = poco.BuildAdapter()
                  .AddParameters("user", this.User.Identity.Name)
                  .AdaptToType<Dto>();