Mapture 1.1.1
dotnet add package Mapture --version 1.1.1
NuGet\Install-Package Mapture -Version 1.1.1
<PackageReference Include="Mapture" Version="1.1.1" />
<PackageVersion Include="Mapture" Version="1.1.1" />
<PackageReference Include="Mapture" />
paket add Mapture --version 1.1.1
#r "nuget: Mapture, 1.1.1"
#:package Mapture@1.1.1
#addin nuget:?package=Mapture&version=1.1.1
#tool nuget:?package=Mapture&version=1.1.1
Mapture
The fastest object mapper for .NET — with safety features no other mapper has.
Disclaimer: This project is an independent implementation and is not affiliated with AutoMapper, Mapster, PanoramicData.Mapper, or any other mapping library.
The Story
You've been here before. Your API maps User to UserDto ten thousand times per second, and one day someone adds a Parent property that points back to itself. The stack overflows. Production goes down. Nobody knows why.
Or maybe it's subtler — a mapping library that phones home with telemetry you never asked for. Or one that only supports .NET 8+, leaving your legacy services stranded.
Mapture was built for teams who are tired of compromise. It's the only .NET mapper that is simultaneously:
- Fastest — benchmarked faster than Mapster, AutoMapper, and PanoramicData.Mapper
- Safest — cycle detection and max-depth enforcement catch infinite recursion before it happens
- Broadest — runs on .NET Framework 4.8, .NET Standard 2.0, .NET 8, and .NET 10
- Cleanest — zero telemetry, zero surprises, familiar API, 30-minute migration from AutoMapper
Benchmark Results
Measured with BenchmarkDotNet on .NET 10.0, mapping a simple object with one nested child. All libraries configured with equivalent mappings, run on the same hardware:
| Rank | Method | Mean | vs Manual | Allocated |
|---|---|---|---|---|
| 🥇 | Manual Mapping | ~17 ns | baseline | 96 B |
| 🥈 | Mapture | ~25 ns | 1.5x | 96 B |
| 🥉 | Mapster | ~27 ns | 1.6x | 96 B |
| 4 | AutoMapper | ~68 ns | 4.0x | 96 B |
| 5 | PanoramicData.Mapper | ~283 ns | 16.9x | 272 B |
How? Mapture compiles expression trees at configuration time and caches the compiled delegates per type pair. Acyclic type graphs get a zero-overhead fast path that skips all cycle/depth tracking. The result: your mapping function runs almost as fast as code you'd write by hand.
Quick Start
1. Install
dotnet add package Mapture
dotnet add package Mapture.Extensions.DependencyInjection
2. Define a Profile
using Mapture;
public class UserProfile : Profile
{
public UserProfile()
{
CreateMap<User, UserDto>()
.ForMember(d => d.FullName, opt => opt.MapFrom(s => s.FirstName + " " + s.LastName))
.Ignore(d => d.InternalId);
}
}
3. Register with DI
using Mapture.Extensions.DependencyInjection;
builder.Services.AddMapture(typeof(Program).Assembly);
4. Map Objects
public class UsersController : ControllerBase
{
private readonly IMapper _mapper;
public UsersController(IMapper mapper) => _mapper = mapper;
[HttpGet]
public IActionResult Get()
{
var users = _userService.GetAll();
return Ok(users.Select(u => _mapper.Map<User, UserDto>(u)));
}
}
That's it. Convention-based matching handles properties with the same name. Custom mappings, ignores, and reverse maps are all one fluent call away.
Feature Comparison
| Feature | Mapture | AutoMapper | Mapster | PanoramicData.Mapper |
|---|---|---|---|---|
| Performance rank | 🥈 | 4th | 🥉 | 5th |
| Convention-based mapping | ✅ | ✅ | ✅ | ✅ |
| Profile system | ✅ | ✅ | ❌ | ✅ |
| ForMember / Ignore | ✅ | ✅ | ✅ | ✅ |
| ReverseMap | ✅ | ✅ | ✅ | ✅ |
| Nested object mapping | ✅ | ✅ | ✅ | ✅ |
| Collection mapping | ✅ | ✅ | ✅ | ✅ |
IEnumerable<TSrc> → List<TDest> |
✅ | ✅ | ✅ | ⚠️ |
Non-nullable → nullable coercion (int→int?, bool→bool?, etc.) |
✅ | ✅ | ✅ | ⚠️ |
Numeric widening / narrowing (int→long, long→int, etc.) |
✅ | ✅ | ✅ | ⚠️ |
In-place update (Map(src, dest) with mismatched nullability) |
✅ | ✅ | ✅ | ⚠️ |
| Enum ↔ string / numeric coercion | ✅ | ✅ | ✅ | ⚠️ |
Cycle detection |
✅ | ❌ | ❌ | ❌ |
| Max depth enforcement | ✅ | ❌ | ❌ | ✅ |
| ConvertUsing | ✅ | ✅ | ✅ | ✅ |
| BeforeMap / AfterMap | ✅ | ✅ | ✅ | ✅ |
| ConstructUsing | ✅ | ✅ | ✅ | ✅ |
| Condition | ✅ | ✅ | ✅ | ✅ |
| MapFrom (Expression) | ✅ | ✅ | ✅ | ✅ |
| MapFrom (string source-member name) | ✅ | ✅ | ❌ | ✅ |
ResolveUsing (Func<TSource, TMember>) |
✅ | ✅ | ✅ | ⚠️ |
| ResolveUsing with destination access | ✅ | ✅ | ⚠️ | ❌ |
| NullSubstitute | ✅ | ✅ | ✅ | ❌ |
| UseValue (constant) | ✅ | ✅ | ✅ | ✅ |
| Configuration validation | ✅ | ✅ | ❌ | ✅ |
| DI integration | ✅ | ✅ | ✅ | ✅ |
| No forced telemetry | ✅ | ❌ | ✅ | ✅ |
| .NET Framework 4.8 | ✅ | ✅ | ✅ | ❌ |
| .NET Standard 2.0 | ✅ | ❌ | ✅ | ❌ |
| .NET 8 / .NET 10 | ✅ | ✅ | ✅ | ⚠️ .NET 10 only |
Safety Features
Cycle Detection
Objects that reference themselves (directly or indirectly) are the #1 cause of
StackOverflowException in mapping libraries. Mapture detects cycles at runtime
and breaks them safely:
var node = new Node { Id = 1 };
node.Parent = node; // Circular reference!
var dto = mapper.Map<Node, NodeDto>(node);
// dto.Parent is null — cycle safely broken, no stack overflow
Max Depth Enforcement
Even without cycles, deeply nested object graphs can exhaust the stack. Mapture caps recursion at a configurable depth:
services.AddMapture(typeof(Program).Assembly, options =>
{
options.MaxDepth = 5; // Stop mapping beyond 5 levels deep
});
Smart Cycle Analysis
Mapture analyzes your type graph at configuration time. Types that cannot have
cycles (no self-referencing paths) get a zero-overhead fast path — no HashSet, no
depth counter, no per-call allocation. You get safety where you need it and raw speed
everywhere else.
Configuration Validation
Catch unmapped properties at startup — not at 3am in production:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>();
});
config.AssertConfigurationIsValid();
// Throws MaptureException if any destination property has no source and isn't ignored
Fluent API
ForMember — Custom Property Mapping
// Strongly-typed expression (preferred, optimized into the compiled delegate)
CreateMap<User, UserDto>()
.ForMember(d => d.FullName,
opt => opt.MapFrom(s => s.First + " " + s.Last));
// By source-member name (matches AutoMapper's string overload)
CreateMap<User, UserDto>()
.ForMember(d => d.DisplayName, opt => opt.MapFrom("FirstName"));
ResolveUsing — Runtime Resolver Delegate
Use when a closure or runtime state prevents authoring an Expression:
CreateMap<User, UserDto>()
.ForMember(d => d.Token, opt => opt.ResolveUsing(s => _tokenService.For(s.Id)));
// Or with access to the current destination value
CreateMap<User, UserDto>()
.ForMember(d => d.AuditTrail, opt => opt.ResolveUsing((s, d) => d.AuditTrail + ";" + s.LastLogin));
NullSubstitute — Default for Null Source Members
CreateMap<User, UserDto>()
.ForMember(d => d.NickName, opt =>
{
opt.MapFrom(s => s.NickName);
opt.NullSubstitute("(anonymous)");
});
Ignore — Skip a Property
CreateMap<User, UserDto>()
.Ignore(d => d.InternalSecret);
ReverseMap — Bidirectional Mapping
CreateMap<User, UserDto>().ReverseMap();
// Now both User→UserDto and UserDto→User work
ConvertUsing — Full Custom Conversion
CreateMap<User, UserDto>()
.ConvertUsing(src => new UserDto
{
Id = src.Id,
Name = src.Name.ToUpperInvariant()
});
BeforeMap / AfterMap — Pre/Post Processing
CreateMap<User, UserDto>()
.AfterMap((src, dest) => dest.Name = dest.Name.Trim());
ConstructUsing — Custom Instantiation
CreateMap<User, UserDto>()
.ConstructUsing(src => new UserDto { Id = src.Id * 10 });
Condition — Conditional Mapping
CreateMap<User, UserDto>()
.ForMember(d => d.Age, opt =>
{
opt.MapFrom(s => s.Age);
opt.Condition(s => s.Age > 0);
});
Automatic Type Coercion
No more ForMember boilerplate just because the destination type is wider:
CreateMap<Order, OrderDto>(); // That's it.
// Source -> Dest property type coercion handled automatically:
// int -> int? / long / long? / decimal / decimal? / double / double?
// long -> int / int? (with overflow check)
// bool -> bool?
// DateTime -> DateTime? / DateTimeOffset?
// decimal -> decimal?
// string -> Enum (Enum.Parse)
// int -> Enum (cast)
// Enum -> string / int
Collection Projection from IEnumerable<T>
If CreateMap<Src, Dest>() is registered, Mapture automatically projects any
IEnumerable<Src> into List<Dest>, Dest[], IList<Dest>, ICollection<Dest>,
or IEnumerable<Dest> — no extra .ToList() calls required:
IEnumerable<User> users = repo.QueryUsers();
List<UserDto> dtos = mapper.Map<IEnumerable<User>, List<UserDto>>(users);
In-place Update Mapping
Use the two-parameter overload to update an existing destination instance — including when source/destination types differ in nullability:
var dto = new OrderDto();
mapper.Map(order, dto); // Coerces int->int?, bool->bool?, etc. on the fly
UseValue — Constant Value
CreateMap<User, UserDto>()
.ForMember(d => d.Source, opt => opt.UseValue("API"));
Migration from AutoMapper
Mapture uses the same API patterns. Most migrations take under 30 minutes:
Step 1 — Replace NuGet Packages
dotnet remove package AutoMapper
dotnet remove package AutoMapper.Extensions.Microsoft.DependencyInjection
dotnet add package Mapture
dotnet add package Mapture.Extensions.DependencyInjection
Step 2 — Find & Replace Namespaces
| Find | Replace |
|---|---|
using AutoMapper; |
using Mapture; |
using AutoMapper.Extensions.Microsoft.DependencyInjection; |
using Mapture.Extensions.DependencyInjection; |
Step 3 — Replace DI Registration
// Before
services.AddAutoMapper(typeof(Startup));
// After
services.AddMapture(typeof(Startup));
Step 4 — Run Tests
Most mappings work identically. The same Profile, CreateMap, ForMember, Ignore,
and ReverseMap patterns are supported.
Configuration Options
services.AddMapture(typeof(Program).Assembly, options =>
{
options.CompatibilityMode = true; // Extra AutoMapper compat behaviors
options.MaxDepth = 10; // Prevent infinite recursion (default: 10)
options.EnableCycleDetection = true; // Detect circular references (default: true)
options.EnableDebugTracing = false; // Debug logging (default: false)
});
Architecture
Mapture.slnx
├── src/
│ ├── Mapture.Core # Core mapping engine
│ ├── Mapture.Compatibility # AutoMapper compatibility layer
│ ├── Mapture.Extensions.DependencyInjection # DI registration extensions
│ └── Mapture.Benchmarks # BenchmarkDotNet performance tests
├── tests/
│ ├── Mapture.Tests # 37 unit tests × 3 frameworks = 111
│ └── Mapture.CompatibilityTests # 3 compat tests × 3 frameworks = 9
├── enterprise/
│ └── Mapture.Enterprise # Metrics & audit (optional)
├── samples/
│ └── Mapture.Sample.Api # Sample ASP.NET Core Web API
└── docs/
└── index.html # Full documentation site
How the Engine Works
- Configuration time — You define mappings via Profiles. Mapture builds a type map dictionary.
- First map call — Mapture compiles an expression tree for the type pair into a native delegate. For acyclic types, this delegate is pure property assignment — no reflection, no dictionary lookups, no allocations beyond the destination object.
- Subsequent calls — The compiled delegate is cached in a thread-static slot. The hot path is: null check → read cached delegate → call. That's it.
For cyclic types (detected at configuration time), Mapture wraps the delegate with cycle detection and depth tracking. You never need to think about it — the engine picks the right path automatically.
Enterprise Extension
Optional enterprise features available in Mapture.Enterprise:
- Mapping metrics — execution counts and latency tracking
- Audit trail — field-level lineage tracking
- No forced telemetry — opt-in only, you control what's collected
License
MIT License. See LICENSE for details.
Disclaimer: This project is an independent implementation and is not affiliated with AutoMapper, Mapster, or any other object mapping library. Common API patterns (IMapper, Profile, CreateMap) are standard industry conventions used for migration compatibility.
| 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 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 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 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. |
| .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 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 is compatible. 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. |
-
.NETFramework 4.8
- No dependencies.
-
.NETStandard 2.0
- No dependencies.
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (4)
Showing the top 4 NuGet packages that depend on Mapture:
| Package | Downloads |
|---|---|
|
Sparkdo.AutoMapper
Sparkdo Mapture 集成库,提供基于 Mapture 的对象映射功能实现 |
|
|
Mapture.Extensions.DependencyInjection
Dependency injection extensions for Mapture. Register IMapper and auto-discover Profiles with a single call to AddMapture(). |
|
|
Mapture.Compatibility
AutoMapper compatibility layer for Mapture. Enables easier migration from AutoMapper with compatible behaviors. |
|
|
Fluxorq
High-performance request dispatching and pipeline framework for .NET, with first-class object mapping powered by Mapture. An original implementation inspired by mediator and middleware patterns. |
GitHub repositories
This package is not used by any popular GitHub repositories.