NMapper 2.1.13
See the version list below for details.
dotnet add package NMapper --version 2.1.13
NuGet\Install-Package NMapper -Version 2.1.13
<PackageReference Include="NMapper" Version="2.1.13" />
<PackageVersion Include="NMapper" Version="2.1.13" />
<PackageReference Include="NMapper" />
paket add NMapper --version 2.1.13
#r "nuget: NMapper, 2.1.13"
#:package NMapper@2.1.13
#addin nuget:?package=NMapper&version=2.1.13
#tool nuget:?package=NMapper&version=2.1.13
NMapper
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 | Versions 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. |
-
.NETStandard 2.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.5)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
-
.NETStandard 2.1
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.5)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
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 |
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.