At some point you will map model A to B, C to D, and so on, until you’re thoroughly tired of it. Most .NET developers reach for AutoMapper—a mature, feature-rich library. There’s also Mapperly: a free, open-source alternative that uses Roslyn source generators to emit plain C# mapping code at compile time.
What that buys you:
Performance: zero reflection in hot paths; simple IL the JIT can optimize well
Safety: compile-time diagnostics for missing/ambiguous mappings (e.g., RMG020)
DX: you can read the generated code when you need insight or a debugger step-through
AutoMapper remains excellent, with a huge ecosystem and powerful features. As of v15, AutoMapper requires a paid commercial license for larger companies; see the pricing page for details. Mapperly remains free and open source.
And actually, I fully support Jimmy Bogard for his decision. However it's also a great opportunity for other libraries to take the stage for anyone that doesn't want to spend money.
For the examples and benchmarks in this repo, we map a domain Dragon to a DragonDto with nested objects and a collection:
csharp
public sealed class Dragon
{
public string Name { get; set; } = string.Empty;
public string Color { get; set; } = string.Empty;
public int AgeYears { get; set; }
public double SizeMeters { get; set; }
public Rider? Rider { get; set; }
public Lair? Lair { get; set; }
public List<Treasure> Hoard { get; set; } = new();
}
csharp
public sealed class DragonDto
{
public string DisplayName { get; set; } = string.Empty;
public string PrimaryColor { get; set; } = string.Empty;
public string Age { get; set; } = string.Empty;
public int LengthCentimeters { get; set; }
public bool IsAncient { get; set; }
public RiderDto? Pilot { get; set; }
public LairDto? Hideout { get; set; }
public List<TreasureDto> HoardItems { get; set; } = new();
}
With Mapperly you keep concise, intention-revealing declarations and let the generator produce the concrete mapping code. You can also open the generated C# to review exactly what runs.
The sample shows nested mapping for Rider and Lair, and a collection of Treasure. Mapperly lets you:
Flatten/unflatten via [MapProperty] between different shapes
Compose generated code with your own [UserMapping] helpers
Get diagnostics when you forget a property (e.g., missing MapProperty), early at compile time
You could, for example, convert units (SizeMeters → LengthCentimeters), compute flags (IsAncient), and merge names (FullName) — all with small helpers that remain easy to test.
If we were to add a CreatedAt datetime field to the dragon like so:
csharp
public sealed class Dragon
{
public string Name { get; set; } = string.Empty;
public string Color { get; set; } = string.Empty;
public int AgeYears { get; set; }
public double SizeMeters { get; set; }
public Rider? Rider { get; set; }
public Lair? Lair { get; set; }
public List<Treasure> Hoard { get; set; } = new();
public DateTime CreatedAt { get; set; }
}
our compiler will tell us:
log
The member CreatedAt on the mapping source type
Benchmarks.Models.DomainModels.Dragon
is not mapped to any member on the mapping
target type Benchmarks.Models.DtoModels.DragonDto
And that's great, because it allows us to know for sure we didn't miss any properties to map, and we have at least specified what to do with every property.
Mapperly: prefer for performance-sensitive or large-batch mappings; when you want readable generated code and compile-time feedback. Also the licence is more permissive and its fully free to use, even for large companies.
AutoMapper: prefer when you need advanced projection features (EF/Linq etc.), a broad ecosystem, or you already have significant profiles configured.
As ever, benchmark with your own models and sizes.