Skip to content

About

Source-generated, zero-allocation, AOT-safe Command→Domain→DTO mapper for .NET. [Map<,>]/[TryMap<,>] on a static partial class — generator emits direct property assignments at compile time. Result<T, MappingError> on the [TryMap] path; integrates with ZeroAlloc.Results and ZeroAlloc.ValueObjects smart constructors.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

283 Commits

Folders and files

Repository files navigation

ZeroAlloc.Mapping

NuGet Build codecov License: MIT AOT GitHub Sponsors

Source-generated object mapper for .NET, an AutoMapper alternative that writes your mapping code at compile time: no reflection, no runtime configuration, NativeAOT-ready.

Why ZeroAlloc.Mapping

  • 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.

Install

dotnet add package ZeroAlloc.Mapping

The generator runs as part of dotnet build. There is no DI registration and no startup configuration.

Quick start

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.

What you get

  • Constructor and property mapping with conversions, renames and per-property customisation.
  • Fallible mapping: [TryMap<,>] returns Result<T, MappingError> instead of throwing, and a throwing smart constructor from ZeroAlloc.ValueObjects comes back as a failed result.
  • Flattening: map Customer.Address.City onto City with [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.

Performance

.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.

Learn more

About

Source-generated, zero-allocation, AOT-safe Command→Domain→DTO mapper for .NET. [Map<,>]/[TryMap<,>] on a static partial class — generator emits direct property assignments at compile time. Result<T, MappingError> on the [TryMap] path; integrates with ZeroAlloc.Results and ZeroAlloc.ValueObjects smart constructors.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages