Source-generated object mapper for .NET, an AutoMapper alternative that writes your mapping code at compile time: no reflection, no runtime configuration, NativeAOT-ready.
- Allocates what hand-written code allocates. A flat record copy allocates 72 B, the same as
new Dst(...)by hand. Update-in-place allocates 0 B, asserted by a test that fails CI on any regression. - 1.3× to 6.9× faster than AutoMapper on single-object mappings, measured in the same BenchmarkDotNet run.
- Works under NativeAOT and trimming. The generated body is plain property reads and a constructor call. The AOT smoke test publishes with trim and AOT warnings treated as errors.
- Mistakes fail the build, not the request. A destination property with no source is compile error ZAMP001, not a runtime exception on first use.
dotnet add package ZeroAlloc.MappingThe generator runs as part of dotnet build. There is no DI registration and no startup configuration.
using ZeroAlloc.Mapping;
var order = OrderMappings.Map(new OrderRequest(42, "rush")); // a new Order
var orders = OrderMappings.Map(new List<OrderRequest> { new(1, "a"), new(2, "b") });
OrderMappings.Map(new OrderRequest(42, "gift wrap"), order); // update in place, 0 B
Console.WriteLine($"{order.Id}: {order.Notes}, {orders.Count} mapped");
public sealed record OrderRequest(int Id, string Notes);
public sealed class Order
{
public int Id { get; set; }
public string Notes { get; set; } = "";
}
[Map<OrderRequest, Order>]
public static partial class OrderMappings
{
public static partial void Map(OrderRequest src, Order existingDst);
}One attribute gives you the single-object mapper and the List<T>, T[], IEnumerable<T> and IReadOnlyList<T> overloads. Declaring the partial void Map(src, existing) adds the update-in-place form. Each generated method is a null check and a direct constructor call or property writes.
- Constructor and property mapping with conversions, renames and per-property customisation.
- Fallible mapping:
[TryMap<,>]returnsResult<T, MappingError>instead of throwing, and a throwing smart constructor from ZeroAlloc.ValueObjects comes back as a failed result. - Flattening: map
Customer.Address.CityontoCitywith[MapProperty]. - Reverse mapping: one
[ReverseMap<,>]declaration gives you both directions. - Polymorphic dispatch: a compile-time type switch over derived types.
- Update in place for EF Core tracked entities and pooled objects.
- Collection overloads generated automatically, with an opt-out.
- Before and after hooks, which cost nothing when empty.
- Culture-aware parsing and strict or case-insensitive matching per mapper.
- IQueryable projection for EF Core, cycle-safe mapping for graphs with back-references, and deep clone.
- Integration: ZeroAlloc.Results powers the
Result<T, MappingError>path.
.NET 10, BenchmarkDotNet, same run. Allocations are exact; the last column is AutoMapper's time divided by ZeroAlloc.Mapping's.
| Scenario | ZeroAlloc.Mapping | AutoMapper | AutoMapper time |
|---|---|---|---|
| Update in place | 0 B | 0 B | 6.9× slower |
| Polymorphic dispatch | 32 B | 32 B | 5.7× slower |
| 1,000-item collection | 78.18 KB | 86.52 KB | 1.5× slower |
Mapperly, the other source-generated mapper, allocates within 0.1 KB of ZeroAlloc.Mapping in every scenario and runs at 0.95× to 1.21× its time. The performance page has all seven scenarios, the hand-written baseline, the per-call allocation budgets CI enforces, and how to reproduce the numbers.
- Documentation site: mapping.zeroalloc.net
- Documentation source: guides, cookbook, the diagnostics reference and testing generated mappers.
- Related packages: ZeroAlloc.Results, ZeroAlloc.ValueObjects, ZeroAlloc.Validation.
- Changelog. Issues and pull requests are welcome on GitHub.
- MIT licensed. See LICENSE.