Mappy.dotNet
4.0.0
dotnet add package Mappy.dotNet --version 4.0.0
NuGet\Install-Package Mappy.dotNet -Version 4.0.0
<PackageReference Include="Mappy.dotNet" Version="4.0.0" />
<PackageVersion Include="Mappy.dotNet" Version="4.0.0" />
<PackageReference Include="Mappy.dotNet" />
paket add Mappy.dotNet --version 4.0.0
#r "nuget: Mappy.dotNet, 4.0.0"
#:package Mappy.dotNet@4.0.0
#addin nuget:?package=Mappy.dotNet&version=4.0.0
#tool nuget:?package=Mappy.dotNet&version=4.0.0
🍁Mappy 4.0
Mappy is a lightweight, convention-first object mapper for modern .NET.
It keeps the common case simple:
var dto = entity.Map<EntityDto>();
Mappy 4.0 focuses on predictable mapping, cached mapping plans, compiled property accessors, nested objects, collections, immutable constructor-based types, circular-reference preservation, explicit configuration, converters, and mapping into existing objects.
Why Mappy?
- Simple API with no mandatory configuration
- Cached mapping plans per source/destination pair
- Compiled property getters and setters
- Nested object mapping
- Collection and array mapping
- Constructor mapping for immutable DTOs and records
- Circular and shared-reference preservation
- Explicit property rename and ignore configuration
- Explicit type converters
MapTofor updating existing objects- Detailed
MappingExceptionerrors - Targets .NET 8, 9, and 10
- No external runtime dependencies
Installation
dotnet add package Mappy.dotNet --version 4.0.0
Basic mapping
var source = new User
{
Id = 1,
Name = "Manoj",
Email = "manoj@example.com"
};
var dto = source.Map<UserDto>();
Properties with matching names and compatible types are mapped automatically.
Nested mapping
var dto = order.Map<OrderDto>();
Mappy recursively maps compatible nested objects:
Order
└── Customer
↓
OrderDto
└── CustomerDto
Collections
var result = users.MapCollection<UserDto>();
Arrays are also supported by the mapping engine:
UserDto[] result = users
.MapCollection<UserDto>()
.ToArray();
Supported destination collection shapes include arrays, List<T>, HashSet<T>, and compatible generic collection interfaces such as IEnumerable<T>, ICollection<T>, IList<T>, and IReadOnlyCollection<T>.
Rename properties
Use strongly typed configuration when source and destination names differ:
var config = new MappingConfiguration()
.Map<User, UserDto>(
source => source.FullName,
destination => destination.Name);
var dto = user.Map<UserDto>(config: config);
The original string-based configuration API is also retained:
var config = new MappingConfiguration();
config.AddPropertyMapping(
typeof(User),
typeof(UserDto),
"FullName",
"Name");
Ignore properties
var config = new MappingConfiguration()
.Ignore<User, UserDto>(x => x.PasswordHash);
var dto = user.Map<UserDto>(config: config);
Or:
config.ExcludeProperty(
typeof(User),
typeof(UserDto),
"PasswordHash");
Explicit converters
Mappy intentionally avoids broad implicit conversions. Register conversions explicitly:
var config = new MappingConfiguration()
.AddConverter<Guid, string>(
value => value.ToString("N"));
var dto = entity.Map<EntityDto>(config: config);
This keeps type behavior predictable.
Mapping into an existing object
For update scenarios:
request.MapTo(entity);
Optional mapping options are supported:
request.MapTo(
entity,
options: new MappingOptions
{
IgnoreNullValues = true
});
This is useful for PATCH-style updates where null values should leave existing destination values unchanged.
Immutable types and records
Mappy 4.0 can map constructor parameters when a destination does not have a parameterless constructor:
public sealed class User
{
public string Name { get; set; } = "";
public int Age { get; set; }
}
public sealed class UserDto(string name, int age)
{
public string Name { get; } = name;
public int Age { get; } = age;
}
Then:
var dto = user.Map<UserDto>();
Circular references
Mappy 4.0 preserves object identity by default.
For:
Parent
└── Child
└── Parent
the mapped graph can retain the same relationship:
var result = source.Map<Destination>();
ReferenceEquals(
result,
result.Child.Parent);
Returns true.
The legacy boolean API remains available:
var result = source.Map<Destination>(
handleCircularReferences: false);
For more control:
var options = new MappingOptions
{
PreserveReferences = true
};
Private properties
For compatibility with earlier Mappy releases, non-public instance properties remain supported by default.
You can explicitly disable them:
var options = new MappingOptions
{
IncludeNonPublicProperties = false
};
var dto = source.Map<UserDto>();
Asynchronous custom mapping
Normal object mapping is synchronous and does not introduce an unnecessary async pipeline.
Async is available when your custom mapping itself requires asynchronous work:
var dto = await source.MapAsync<UserDto>(async destination =>
{
destination.DisplayName =
await GetDisplayNameAsync(source.Id);
});
For collections:
var result = await users.MapCollectionAsync<UserDto>(
async destination =>
{
destination.DisplayName =
await GetDisplayNameAsync(destination.Id);
});
Type safety
Mappy maps directly assignable or explicitly supported values.
For incompatible types:
public class Source
{
public int Id { get; set; }
}
public class Destination
{
public DateTime Id { get; set; }
}
mapping fails with a MappingException instead of silently performing an unexpected conversion.
For intentional conversions, register a converter.
Performance
Mappy 4.0 separates mapping-plan construction from mapping execution.
The first mapping for a source/destination pair builds and caches metadata:
Source + Destination
↓
Mapping plan
↓
Compiled property accessors
↓
Cached
↓
Fast repeated mapping
Property getters and setters are compiled once rather than using PropertyInfo.GetValue and SetValue for every mapped property.
For trustworthy performance comparisons, the repository includes a dedicated benchmark project. Benchmarks should be run in Release mode on the target runtime and hardware rather than relying on a single stopwatch measurement.
Design goals
Mappy deliberately does not require:
- a dependency injection container
- a global service provider
- a large configuration system
- runtime source-generation tooling
- implicit conversion of arbitrary types
- external runtime packages
The goal is a small API with strong internals.
Target frameworks
Mappy targets:
- .NET 8
- .NET 9
- .NET 10
License
Mappy is released under the MIT License.
| Product | Versions 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. |
-
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.
Mappy 4.0 introduces cached mapping plans, compiled property accessors, explicit configuration, converters, MapTo, constructor mapping, reference preservation, improved collections, and detailed MappingException errors.