Skip to content

Configuration

Martin Zahálka edited this page Feb 18, 2026 · 2 revisions

Configuration

ZMapper uses a fluent API to configure mappings. This page covers all available configuration options.

CreateMap — Define a Mapping

The CreateMap<TSource, TDestination>() method registers a mapping between two types:

config.CreateMap<Source, Destination>();

Properties with matching names are mapped automatically (convention-based mapping).

ForMember — Custom Property Mapping

Use ForMember when source and destination property names differ, or when you need custom transformation logic:

config.CreateMap<OrderDto, Order>()
    .ForMember(dest => dest.OrderTotal,
               opt => opt.MapFrom(src => src.Items.Sum(i => i.Price)));

Navigation Properties (Flattening)

Map from nested objects using navigation properties:

config.CreateMap<InvoiceEntity, InvoiceDto>()
    // Flatten: src.Client.CompanyName -> dest.ClientName
    .ForMember(dest => dest.ClientName,
               opt => opt.MapFrom(src => src.Client != null ? src.Client.CompanyName : ""));

Null-Coalescing

Handle nullable source values with fallback defaults:

config.CreateMap<InvoiceEntity, InvoiceDto>()
    // DateTime? -> DateTime with fallback
    .ForMember(dest => dest.IssueDate,
               opt => opt.MapFrom(src => src.IssueDate ?? DateTime.UtcNow));

String Transformations

Apply transformations inline:

config.CreateMap<UserDto, User>()
    .ForMember(dest => dest.EmailAddress,
               opt => opt.MapFrom(src => src.Email.ToUpper()));

Ignore — Skip Properties

Prevent specific destination properties from being mapped:

config.CreateMap<UserDto, User>()
    .ForMember(dest => dest.PasswordHash, opt => opt.Ignore())
    .ForMember(dest => dest.InternalId, opt => opt.Ignore());

Ignored properties retain their default values (null, 0, false, etc.).

IgnoreNonExisting — Suppress Unmapped Warnings

By default, ZMapper emits a compile-time warning (ZMAP001) for destination properties without a matching source:

warning ZMAP001: Destination property 'Address' on type 'Destination'
has no matching source property on 'Source'.

You have two options:

Option 1: Ignore Individual Properties

config.CreateMap<Source, Destination>()
    .ForMember(dest => dest.Address, opt => opt.Ignore());

Option 2: Ignore All Non-Matching Properties

config.CreateMap<Source, Destination>()
    .IgnoreNonExisting();  // All non-matching properties silently keep defaults

This is especially useful when hooks (BeforeMap/AfterMap) set destination-only properties:

config.CreateMap<InvoiceDto, Invoice>()
    .IgnoreNonExisting()  // CreatedAt, ProcessedBy set by hooks
    .BeforeMap((src, dest) => dest.CreatedAt = DateTime.UtcNow)
    .AfterMap((src, dest) => dest.ProcessedBy = "ZMapper");

When — Conditional Mapping

Map a property only when a condition is met:

config.CreateMap<ProductDto, Product>()
    .ForMember(dest => dest.Price, opt =>
    {
        opt.MapFrom(src => src.Price);
        opt.When(src => src.Price > 0);  // Only map positive prices
    })
    .ForMember(dest => dest.Description, opt =>
    {
        opt.MapFrom(src => src.Description!);
        opt.When(src => src.Description != null);  // Only map non-null
    });

When the condition evaluates to false, the destination property keeps its default value.

ReverseMap — Bidirectional Mapping

Create mappings in both directions with a single call:

config.CreateMap<OrderDto, Order>()
    .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.OrderId))
    .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.CustomerName))
    .ReverseMap();

// Now both OrderDto -> Order AND Order -> OrderDto work
var order = mapper.Map<OrderDto, Order>(orderDto);
var dto = mapper.Map<Order, OrderDto>(order);

Note: ReverseMap() automatically inverts ForMember configurations. Ignored members are excluded from the reverse mapping. IgnoreNonExisting() is propagated to the reverse direction.

BeforeMap / AfterMap — Hooks

Execute custom logic before or after mapping:

config.CreateMap<InvoiceDto, Invoice>()
    .ForMember(dest => dest.InvoiceId, opt => opt.MapFrom(src => src.Id))
    .BeforeMap((src, dest) =>
    {
        // Runs BEFORE property mapping
        dest.CreatedAt = DateTime.UtcNow;
    })
    .AfterMap((src, dest) =>
    {
        // Runs AFTER property mapping
        dest.ProcessedBy = "ZMapper";
        dest.TotalWithTax = dest.Total * 1.21m;
    });

Common use cases:

  • Setting audit fields (timestamps, user info)
  • Computing derived values after mapping
  • Logging or validation
  • Normalizing data before mapping

Inheritance — Base Class Properties

ZMapper automatically maps properties from the entire inheritance chain — no extra configuration needed:

public abstract class BaseEntity
{
    public int Id { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
}

public class Product : BaseEntity
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

// Id, CreatedAt, UpdatedAt from BaseEntity are mapped automatically
config.CreateMap<ProductDto, Product>();

This works with any inheritance depth (e.g., BaseEntity -> AuditableEntity -> Product).

Nullable Value Types

When the source has nullable value types and the destination has non-nullable types, ZMapper safely maps using ?? default:

// Source: DateTime? IssueDate -> Destination: DateTime IssueDate
// Generated code: destination.IssueDate = source.IssueDate ?? default;
config.CreateMap<NullableSource, NonNullableDestination>();

Map to Existing Instance

Map into an already-constructed object instead of creating a new one:

var existingUser = GetFromDatabase();
mapper.Map<UserDto, User>(dto, existingUser);
// existingUser's properties are updated in-place

Chaining Configuration

All configuration methods return the mapping expression, so you can chain them fluently:

config.CreateMap<OrderDto, Order>()
    .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.OrderId))
    .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.CustomerName))
    .ForMember(dest => dest.InternalNote, opt => opt.Ignore())
    .IgnoreNonExisting()
    .BeforeMap((src, dest) => dest.CreatedAt = DateTime.UtcNow)
    .AfterMap((src, dest) => dest.Total = dest.Items.Sum(i => i.Price))
    .ReverseMap();

Next Steps

Clone this wiki locally