AtomMapper 1.2.5

dotnet add package AtomMapper --version 1.2.5
                    
NuGet\Install-Package AtomMapper -Version 1.2.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AtomMapper" Version="1.2.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AtomMapper" Version="1.2.5" />
                    
Directory.Packages.props
<PackageReference Include="AtomMapper" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AtomMapper --version 1.2.5
                    
#r "nuget: AtomMapper, 1.2.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AtomMapper@1.2.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AtomMapper&version=1.2.5
                    
Install as a Cake Addin
#tool nuget:?package=AtomMapper&version=1.2.5
                    
Install as a Cake Tool

AtomMapper

A high-performance, lightweight object mapper for .NET — designed to feel familiar to AutoMapper users while delivering significantly better runtime performance.

NuGet License: MIT .NET


Why AtomMapper?

If you have been using AutoMapper, you already know the pattern: define profiles, call CreateMap, use ForMember. That API is intuitive and productive.

However, AutoMapper introduced a commercial licensing model in v13, which pushed many teams to look for alternatives. AtomMapper was built to fill that gap — offering the same familiar API so migration is straightforward, while being fully open-source under the MIT license.

Beyond licensing, AtomMapper takes a different technical approach. Instead of resolving mappings through reflection at every call, AtomMapper compiles all expression trees once at startup into direct Func<TSource, TDestination> delegates. At call time, there is no reflection, no dictionary traversal, and no delegate chain — just a single compiled function invocation. This makes AtomMapper measurably faster than AutoMapper across all scenarios.


Benchmark

Measured with BenchmarkDotNet on .NET 10, mapping Entry → TransactionEntryDto (3 custom members + 1 convention member):

Mapper Single object 100 objects Allocated (100)
AtomMapper 13 ns 967 ns 7,256 B
AutoMapper 33 ns 1,415 ns 8,592 B
Mapster 14 ns 960 ns 7,256 B
Mapperly* 6 ns 851 ns 7,392 B

*Mapperly is a compile-time source generator — it emits plain C# methods that the JIT can inline. AtomMapper is the fastest runtime mapper.


Features

  • Convention-based mapping — properties with matching names and compatible types are mapped automatically
  • Custom member mapping — override any member with a MapFrom expression
  • Ignore members — exclude destination members from mapping
  • Reverse mapping — generate a convention-based reverse map with a single call
  • Nested object mapping — automatically maps nested objects when a mapping for the nested types is registered
  • Collection mapping — maps IEnumerable<T>, List<T>, ICollection<T>, and arrays of mapped types
  • In-place / update mapping — map onto an existing destination instance
  • Single type-parameter shorthandMap<TDestination>(object) infers the source type at runtime
  • Profile-based configuration — group related mappings into profiles, just like AutoMapper
  • DI-friendly — works with any DI container (Microsoft.Extensions.DI, Autofac, etc.)

Installation

dotnet add package AtomMapper

Getting Started

1. Define a profile

using AtomMapper;

public class OrderMappingProfile : IMapperProfile
{
    public void Register(MappingExpressionRegistry registry)
    {
        registry.CreateMap<Order, OrderDto>()
            .ForMember(d => d.CustomerFullName,
                       o => o.MapFrom(s => $"{s.FirstName} {s.LastName}"))
            .ForMember(d => d.TotalPrice,
                       o => o.MapFrom(s => s.Items.Sum(i => i.Price)));
    }
}

2. Create the mapper

IMapper mapper = MapperFactory.Create(
    new OrderMappingProfile(),
    new ProductMappingProfile()
);

Build the mapper once at startup and reuse it for the lifetime of your application. All expression trees are compiled during Create — mapping calls themselves have near-zero overhead.

3. Map objects

// Map to a new instance
var dto = mapper.Map<Order, OrderDto>(order);

// Shorthand — infers source type at runtime
var dto = mapper.Map<OrderDto>(order);

// Map a collection — works for any registered element type
IEnumerable<OrderDto> dtos = mapper.Map<IEnumerable<Order>, IEnumerable<OrderDto>>(orders);

// Ask for a concrete collection type directly
List<OrderDto> list = mapper.Map<IEnumerable<Order>, List<OrderDto>>(orders);

// Update an existing instance in-place
mapper.Map(order, existingDto);

Feature Guide

Convention mapping

Properties that share the same name and a compatible type are mapped automatically — no configuration needed.

public class Product { public int Id { get; set; } public string Name { get; set; } }
public class ProductDto { public int Id { get; set; } public string Name { get; set; } }

registry.CreateMap<Product, ProductDto>(); // Id and Name mapped automatically

Custom member mapping with MapFrom

Use ForMember + MapFrom to override how a specific destination member is populated. The expression is compiled into the mapping function — no reflection at call time.

registry.CreateMap<Entry, TransactionEntryDto>()
    .ForMember(d => d.CurrencyCode,  o => o.MapFrom(s => s.Currency.Symbol))
    .ForMember(d => d.DebitAmount,   o => o.MapFrom(s => s.Amount > 0 ? s.Amount : 0))
    .ForMember(d => d.CreditAmount,  o => o.MapFrom(s => s.Amount < 0 ? Math.Abs(s.Amount) : 0));

Ignoring members

Excluded members retain their default value in the destination object.

registry.CreateMap<ServiceCostDto, ServiceCost>()
    .ForMember(d => d.ServiceType, o => o.Ignore());

Reverse mapping

ReverseMap generates a convention-based reverse mapping from TDestination back to TSource. Custom ForMember rules are intentionally not reversed.

registry.CreateMap<Quotation, QuotationDto>()
    .ForMember(d => d.ReserveNo, o => o.MapFrom(s => s.ConvertedToReserve!.ReserveNO))
    .ReverseMap(); // also registers QuotationDto → Quotation (convention only)

var quotation = mapper.Map<QuotationDto, Quotation>(dto);

Nested object mapping

Register a mapping for the nested types and AtomMapper will use it automatically when it encounters a property whose type has a registered mapping.

registry.CreateMap<Order, OrderDto>();
registry.CreateMap<Address, AddressDto>(); // Order.ShippingAddress → OrderDto.ShippingAddress

No extra configuration required — ShippingAddress is detected and mapped automatically, with a null guard.

Collection mapping

Collections of mapped types are handled automatically. The destination collection type is respected.

// Source:      IEnumerable<OrderItem>
// Destination: List<OrderItemDto>
// Element mapping must be registered: CreateMap<OrderItem, OrderItemDto>()

registry.CreateMap<Order, OrderDto>();
registry.CreateMap<OrderItem, OrderItemDto>();

// Order.Items (IEnumerable<OrderItem>) → OrderDto.Items (List<OrderItemDto>) ✓

Supported destination collection types: IEnumerable<T>, ICollection<T>, IList<T>, List<T>, T[].

Mapping collections directly

Register the element mapping once and pass any compatible collection to Map:

registry.CreateMap<Order, OrderDto>();

// Any of these work without extra registration:
IEnumerable<OrderDto> a = mapper.Map<IEnumerable<Order>, IEnumerable<OrderDto>>(orders);
List<OrderDto>        b = mapper.Map<IEnumerable<Order>, List<OrderDto>>(orders);
OrderDto[]            c = mapper.Map<IEnumerable<Order>, OrderDto[]>(orders);

Supported destination collection types: IEnumerable<T>, ICollection<T>, IList<T>, List<T>, T[].

In-place mapping

Map onto an existing instance, updating only the convention-mapped properties.

mapper.Map(command, existingEntity);

Dependency Injection

Microsoft.Extensions.DependencyInjection

builder.Services.AddSingleton<IMapper>(_ => MapperFactory.Create(
    new OrderMappingProfile(),
    new ProductMappingProfile()
));

Autofac

builder.Register(_ => MapperFactory.Create(
    Assembly.GetExecutingAssembly()
        .GetTypes()
        .Where(t => !t.IsAbstract && t.IsAssignableTo(typeof(IMapperProfile)))
        .Select(t => (IMapperProfile)Activator.CreateInstance(t)!)
        .ToArray()
)).As<IMapper>().SingleInstance();

Migrating from AutoMapper

AtomMapper is intentionally designed to minimise migration friction. The table below shows the equivalent API:

AutoMapper AtomMapper
cfg.CreateMap<Src, Dest>() registry.CreateMap<Src, Dest>()
.ForMember(d => d.Prop, o => o.MapFrom(s => ...)) identical
.ForMember(d => d.Prop, o => o.Ignore()) identical
.ReverseMap() identical
mapper.Map<Dest>(source) identical
mapper.Map<Src, Dest>(source) identical
mapper.Map(source, dest) identical
mapper.Map<IEnumerable<Dest>>(source) mapper.Map<IEnumerable<Src>, IEnumerable<Dest>>(source)
new MapperConfiguration(cfg => ...) MapperFactory.Create(profiles)
Profile base class IMapperProfile interface

The main difference is that AutoMapper uses a Profile base class while AtomMapper uses an IMapperProfile interface, and configuration is passed directly to MapperFactory.Create rather than a MapperConfiguration wrapper.


License

MIT © Ali

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.5 102 5/20/2026
1.2.4 123 5/13/2026
1.2.3 93 5/12/2026
1.2.2 92 5/12/2026
1.2.1 108 4/18/2026
1.2.0 103 4/7/2026
1.1.0 95 4/7/2026
1.0.0 107 4/6/2026