Frictionless object mapping
$ dotnet add package Soenneker.Gen.Adapt
Soenneker.Gen.AdaptA modern, high-performance C# source generator for compile-time object mapping; a zero-overhead replacement for AutoMapper, Mapperly, Mapster, etc.
Adapt<T>() on your objectsdotnet add package Soenneker.Gen.Adapt
Once installed, the generator automatically detects usage of Adapt() and generates extension methods for your types:
public class UserDto
{
public string Name { get; set; }
public int Age { get; set; }
public int Id { get; set; } // This property will be ignored since it doesn't exist in UserModel
}
public class UserModel
{
public string Name { get; set; }
public int Age { get; set; }
}
// Map objects with a simple extension method
var dto = new UserDto { Name = "John", Age = 30 };
UserModel model = dto.Adapt<UserModel>(); // just one line!
If the properties match by name and can be converted, it maps them. If for some reason the source generator cannot build the extension method, an Adapt() extension method will not be generated.
The generator creates extension methods with direct property assignments:
public static partial class GenAdapt
{
public static UserModel Adapt(this UserDto source)
{
var target = new UserModel();
target.Name = source.Name;
target.Age = source.Age;
return target;
}
}
List<T>, IEnumerable<T>, arrays with element conversionDictionary<TKey, TValue> with key/value conversionAdapt<T>())TL;DR: The generated code is generally faster than hand-written mapping code due to advanced object cloning.
AdaptViaReflection<TDest>())For generic type parameters or abstract base classes where concrete types are only known at runtime:
List<SourceItem> to List<DestItem> by recursively converting each itemList<T>, IEnumerable<T>, ICollection<T>, IList<T>Adapt<T>() for better performance when types are known at compile timeTL;DR: Soenneker.Gen.Adapt is the fastest mapping library.
| Method | Mean | Ratio | Allocated | Alloc Ratio |
|---|---|---|---|---|
| Soenneker.Gen.Adapt | 4.954 ns | baseline | 40 B | |
| AutoMapper | 34.880 ns | 7.14x slower | 40 B | 1.00x more |
| Mapster | 12.639 ns | 2.59x slower | 40 B | 1.00x more |
| Mapperly | 5.055 ns | 1.03x slower | 40 B | 1.00x more |
| Facet | 6.068 ns | 1.24x slower | 40 B | 1.00x more |
| Method | Mean | Ratio | Allocated | Alloc Ratio |
|---|---|---|---|---|
| Soenneker.Gen.Adapt | 4.746 ns | baseline | 32 B | |
| AutoMapper | 36.247 ns | 7.72x slower | 32 B | 1.00x more |
| Mapster | 15.796 ns | 3.37x slower | 72 B | 2.25x more |
| Mapperly | 5.064 ns | 1.08x slower | 32 B | 1.00x more |
| Facet | 6.708 ns | 1.43x slower | 32 B | 1.00x more |
| Method | Mean | Ratio | Allocated | Alloc Ratio |
|---|---|---|---|---|
| Soenneker.Gen.Adapt | 38.83 ns | baseline | 320 B | |
| AutoMapper | 86.14 ns | 2.24x slower | 328 B | 1.02x more |
| Mapster | 66.15 ns | 1.72x slower | 320 B | 1.00x more |
| Mapperly | 41.54 ns | 1.08x slower | 320 B | 1.00x more |
| Method | Mean | Ratio | Allocated | Alloc Ratio |
|---|---|---|---|---|
| Soenneker.Gen.Adapt | 3.735 ns | baseline | 24 B | |
| AutoMapper | 47.883 ns | 12.93x slower | 112 B | 4.67x more |
| Mapster | 49.255 ns | 13.30x slower | 320 B | 13.33x more |
| Mapperly | 4.221 ns | 1.14x slower | 24 B | 1.00x more |
| Facet | 4.521 ns | 1.22x slower | 24 B | 1.00x more |
| Method | Mean | Ratio | Allocated | Alloc Ratio |
|---|---|---|---|---|
| Soenneker.Gen.Adapt | 6.549 μs | baseline | 46.93 KB | |
| AutoMapper | 10.655 μs | 1.64x slower | 55.27 KB | 1.18x more |
| Mapster | 7.196 μs | 1.11x slower | 46.93 KB | 1.00x more |
| Mapperly | 6.974 μs | 1.07x slower | 46.93 KB | 1.00x more |
| Method | Mean | Ratio | Allocated | Alloc Ratio |
|---|---|---|---|---|
| Soenneker.Gen.Adapt | 1.033 μs | baseline | 7.87 KB | |
| AutoMapper | 1.690 μs | 1.65x slower | 9.17 KB | 1.17x more |
| Mapster | 1.175 μs | 1.14x slower | 7.87 KB | 1.00x more |
| Mapperly | 1.143 μs | 1.11x slower | 7.87 KB | 1.00x more |
The generator creates multiple files per source type (e.g., Adapt.BasicSource.g.cs, Adapt.UserDto.g.cs). To inspect the generated code, add this to your .csproj:
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
Generated files will appear in Project -> Dependencies -> Analyzers -> Soenneker.Gen.Adapt
⚠️ Note: Source Generators don’t work transitively across project references.
Any project that calls Adapt() must include its own direct package reference.