NMapper 2.1.13

There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package NMapper --version 2.1.13
                    
NuGet\Install-Package NMapper -Version 2.1.13
                    
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="NMapper" Version="2.1.13" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NMapper" Version="2.1.13" />
                    
Directory.Packages.props
<PackageReference Include="NMapper" />
                    
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 NMapper --version 2.1.13
                    
#r "nuget: NMapper, 2.1.13"
                    
#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 NMapper@2.1.13
                    
#: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=NMapper&version=2.1.13
                    
Install as a Cake Addin
#tool nuget:?package=NMapper&version=2.1.13
                    
Install as a Cake Tool

NMapper

Version Downloads Buy Me a Coffee

NMapper is a lightweight, explicit object mapping library for .NET.

It favors simple, testable C# mapping code over conventions, profiles, and hidden runtime behavior.

Why NMapper?

With NMapper, mappings are just code:

  • Explicit mapping classes in plain C#
  • Refactor-safe and easy to debug
  • Nested mappings through IMappingContext
  • Automatic collection and array mapping
  • Dependency injection support
  • No convention-based property matching
  • No "magic" configuration model

If you want mapping logic to stay visible, reviewable, and easy to test, NMapper is designed for that.

Download and Install NMapper

This library is available on NuGet: https://www.nuget.org/packages/NMapper/ Use the following command to install NMapper using the NuGet Package Manager Console:

PM> Install-Package NMapper

Or with the .NET CLI:

dotnet add package NMapper

NMapper supports .NET Standard 2.0 and higher.

Quick Start

1. Define your models

public class Person
{
    public int Id { get; set; }
    public string? Name { get; set; }
}

public class PersonDto
{
    public int Id { get; set; }
    public string? Name { get; set; }
}

2. Create a mapping

Implement IMapping<TSource, TTarget>:

using NMapper;

public sealed class PersonMapping : IMapping<Person, PersonDto>
{
    public PersonDto Map(Person source)
    {
        return new PersonDto
        {
            Id = source.Id,
            Name = source.Name,
        };
    }
}

3. Create a mapper and map an object

using NMapper;

var mapper = new Mapper(new PersonMapping());

var person = new Person
{
    Id = 1,
    Name = "John Doe",
};

var dto = mapper.Map<PersonDto>(person);

// dto.Id == 1
// dto.Name == "John Doe"

That is the core idea of NMapper: you write the mapping once as a normal C# class, register it, and call Map<TTarget>().

Two-Way Mapping

If you want mapping in both directions, implement both interfaces on the same class:

using NMapper;

public sealed class PersonMapping :
    IMapping<Person, PersonDto>,
    IMapping<PersonDto, Person>
{
    public PersonDto Map(Person source)
    {
        return new PersonDto
        {
            Id = source.Id,
            Name = source.Name,
        };
    }

    public Person Map(PersonDto source)
    {
        return new Person
        {
            Id = source.Id,
            Name = source.Name,
        };
    }
}

Usage:

var mapper = new Mapper(new PersonMapping());

var dto = mapper.Map<PersonDto>(person);
var person2 = mapper.Map<Person>(dto);

Nested Mappings

If a mapping needs to call other mappings, implement IMappingWithContext<TSource, TTarget>.

public class Country
{
    public int Id { get; set; }
    public string? Name { get; set; }
}

public class CountryDto
{
    public int Id { get; set; }
    public string? Name { get; set; }
}

public class Person
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public Country? Country { get; set; }
}

public class PersonDto
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public CountryDto? Country { get; set; }
}

public sealed class CountryMapping : IMapping<Country, CountryDto>
{
    public CountryDto Map(Country source)
    {
        return new CountryDto
        {
            Id = source.Id,
            Name = source.Name,
        };
    }
}

public sealed class PersonMapping : IMappingWithContext<Person, PersonDto>
{
    public PersonDto Map(Person source, IMappingContext context)
    {
        return new PersonDto
        {
            Id = source.Id,
            Name = source.Name,
            Country = context.Map<CountryDto?>(source.Country),
        };
    }
}

Register both mappings:

var mapper = new Mapper(
    new CountryMapping(),
    new PersonMapping());

Now Person -> PersonDto can delegate Country -> CountryDto to the mapper.

Collections and Arrays

Collections and arrays are mapped automatically as long as an element mapping exists.

var persons = new[]
{
    new Person { Id = 1, Name = "John Doe" },
    new Person { Id = 2, Name = "Jane Doe" },
};

var mapper = new Mapper(new PersonMapping());

PersonDto[] personDtos = mapper.Map<PersonDto[]>(persons);
IEnumerable<PersonDto>? personDtoEnumerable = mapper.Map<IEnumerable<PersonDto>>(persons);
HashSet<PersonDto>? personDtoSet = mapper.Map<HashSet<PersonDto>>(persons);

You only define the item mapping once. NMapper handles the collection conversion. Common targets such as arrays, List<T>, HashSet<T>, Collection<T>, IEnumerable<T>, ICollection<T>, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, and ISet<T> are supported.

Polymorphic Sources

The generic overload Map<TSource, TTarget>() respects the runtime type of reference-type inputs.

Person person = new Employee { Name = "Jane Doe" };

PersonDto dto = mapper.Map<Person, PersonDto>(person);

If an Employee -> PersonDto mapping is registered, NMapper will use it for the example above.

Registration Options

Register mappings directly

var mapper = new Mapper(
    new PersonMapping(),
    new CountryMapping());

You can also register mappings after construction:

IMapper mapper = new Mapper();

mapper.RegisterMapping(new PersonMapping());
mapper.RegisterMapping(new CountryMapping());

Or register a mapping delegate:

IMapper mapper = new Mapper();

mapper.RegisterMapping<Person, PersonDto>(source => new PersonDto
{
    Id = source.Id,
    Name = source.Name,
});

Register with dependency injection

NMapper integrates with Microsoft.Extensions.DependencyInjection.

using Microsoft.Extensions.DependencyInjection;
using NMapper;

var services = new ServiceCollection();

services.AddMapping(options =>
{
    options.Mappings.ScanAssembly(typeof(PersonMapping).Assembly);
});

var serviceProvider = services.BuildServiceProvider();
var mapper = serviceProvider.GetRequiredService<IMapper>();

You can also add mappings manually:

services.AddMapping(options =>
{
    options.Mappings.Add(new PersonMapping(), new CountryMapping());
});

Per-Call Options

You can override mapping behavior per call:

var dto = mapper.Map<PersonDto>(person, options =>
{
    options.EnableRecursionHandling = true;
});

This is useful when only specific mapping operations need additional safeguards.

Recursion Handling

By default, NMapper does not track references while mapping. That keeps mapping fast and allocation-light for simple object graphs.

If you map circular object graphs, enable recursion handling:

var mapper = new Mapper(new MapperOptions
{
    EnableRecursionHandling = true,
    Mappings = new IMapping[]
    {
        new PersonMapping(),
        new CountryMapping(),
    }
});

You can also configure a maximum depth:

var mapper = new Mapper(new MapperOptions
{
    EnableRecursionHandling = true,
    MaxDepth = 10,
    ThrowIfMaxDepthExceeded = true,
    Mappings = new IMapping[]
    {
        new PersonMapping(),
        new CountryMapping(),
    }
});

Recursion handling has a runtime cost and should only be enabled when needed.

Exceptions

NMapper throws explicit exceptions when something is missing or invalid:

Exception Meaning
DuplicateMappingException More than one mapping was registered for the same source and target type.
MissingMappingException No mapping exists for the requested source and target type.
MappingException A mapping failed during execution.
AggregateException Multiple nested mappings failed during one operation.

Example:

try
{
    var dto = mapper.Map<PersonDto>(person);
}
catch (MissingMappingException ex)
{
    Console.WriteLine(ex.Message);
}

Design Philosophy

NMapper treats mapping as application code, not configuration.

That means:

  • Mapping behavior is explicit
  • The implementation is visible in your codebase
  • Debugging happens in normal C# code
  • Refactoring works naturally
  • Complex mappings stay maintainable because composition is explicit

If a mapping is important enough to exist, it is important enough to be code you can read.

Thank You

Thanks to everyone who has contributed to this project.

If you find a bug or want to propose a feature, feel free to open an issue on GitHub.

We'd also like to thank nabinked for leaving us the project name and working title NMapper.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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 was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
2.2.0-pre 93 4/2/2026
2.1.13 89 4/2/2026
2.1.11-pre 85 4/2/2026
2.1.9-pre 80 4/2/2026
2.1.8-pre 87 3/30/2026
2.1.7-pre 84 3/29/2026
2.1.6-pre 80 3/29/2026
2.1.5-pre 91 3/27/2026
2.1.4-pre 83 3/27/2026
2.1.3-pre 79 3/25/2026
2.1.2-pre 96 2/5/2026
2.1.1-pre 142 1/24/2026
2.1.0-pre 92 1/24/2026
2.0.23 114 1/24/2026
2.0.21-pre 99 1/22/2026
2.0.20-pre 101 1/22/2026
2.0.19-pre 103 1/15/2026
2.0.18-pre 98 1/11/2026
2.0.17-pre 96 1/11/2026
2.0.16-pre 105 1/9/2026
Loading failed

2.1
- Added support for mapping into more collection targets, including sets and concrete collection types.
- Improved FastCollectionFactory for thread safety and lower-allocation collection mapping.
- Generic `Map<TSource, TTarget>()` now respects runtime types for polymorphic reference mappings.

2.0
- New mapper IMapper and mapping interfaces IMapping<TSource, TTarget>.
- Handle circular references during mapping.

1.0
- Initial release by nabinked.