OpenAutoMapper 1.0.0
dotnet add package OpenAutoMapper --version 1.0.0
NuGet\Install-Package OpenAutoMapper -Version 1.0.0
<PackageReference Include="OpenAutoMapper" Version="1.0.0" />
<PackageVersion Include="OpenAutoMapper" Version="1.0.0" />
<PackageReference Include="OpenAutoMapper" />
paket add OpenAutoMapper --version 1.0.0
#r "nuget: OpenAutoMapper, 1.0.0"
#:package OpenAutoMapper@1.0.0
#addin nuget:?package=OpenAutoMapper&version=1.0.0
#tool nuget:?package=OpenAutoMapper&version=1.0.0
OpenAutoMapper
A Native AOT-first, high-performance object mapper for .NET — inspired by AutoMapper, powered by Roslyn source generators.
Why OpenAutoMapper?
.NET is moving fast. Native AOT, trimming, and source generators are the new baseline for high-performance applications. But the most popular object mapper — AutoMapper — was designed in a pre-AOT world. It relies on runtime reflection, Expression.Compile(), and dynamic code generation, which means:
- It breaks under
PublishAot=true—Expression.Compile()throwsPlatformNotSupportedExceptionin NativeAOT. - It has measurable startup cost — reflection-based configuration scanning adds 30-50 ms before the first request.
- It allocates on every map — delegate dispatch and internal bookkeeping create GC pressure on hot paths.
- Errors surface at runtime — unmapped properties, misconfigured profiles, and type mismatches only fail when the mapping runs, not when you build.
OpenAutoMapper solves all four problems. The Roslyn source generator emits plain C# mapping code at compile time — the same code you would write by hand. There is nothing to compile at runtime, nothing to reflect over, and nothing that can surprise you in production. Errors become compiler diagnostics. Performance matches hand-written code. And it works everywhere .NET runs, including NativeAOT, Blazor WASM, and trimmed microservices.
On top of the technical issues, AutoMapper's shift from a permissive open-source license to a more restrictive model raised concerns for teams that depend on predictable licensing for their infrastructure libraries. OpenAutoMapper is and will remain MIT-licensed — no license changes, no usage restrictions, no surprises.
If you already use AutoMapper, migrating is a namespace change — the API is identical by design.
Quick Start
1. Install the NuGet package
dotnet add package OpenAutoMapper
2. Define a mapping profile
using OpenAutoMapper;
public class OrderProfile : Profile
{
public OrderProfile()
{
CreateMap<Order, OrderDto>();
}
}
3. Create a configuration and mapper
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<OrderProfile>();
});
var mapper = config.CreateMapper();
4. Map objects
var order = new Order { Id = 1, CustomerName = "Jane Doe", Total = 49.99m };
var dto = mapper.Map<OrderDto>(order);
No reflection. No runtime code generation. Everything is emitted at compile time by the Roslyn source generator.
Key Features
- AOT-safe by design — fully compatible with
PublishAot=trueandTrimMode=full - Zero reflection at runtime — all mapping code is generated at compile time as plain C#
- Zero-allocation flat maps — simple property-to-property maps avoid heap allocations where possible
- Direct extension methods —
source.MapToDto()calls bypass interface dispatch for maximum throughput on hot paths - AutoMapper-compatible API —
Profile,CreateMap,ForMember,IMapper,ProjectTo<T>()all work as expected - Compile-time diagnostics — unmapped properties, open generics, and misconfiguration are reported as compiler warnings/errors (OM-series codes)
- EF Core projections —
CreateProjection<S,D>()andProjectTo<T>()translate to expression trees for efficient SQL generation - Dependency injection —
services.AddAutoMapper()works as a drop-in replacement - Init-only & record types —
initproperties and positional records are mapped correctly - Constructor mapping —
ForCtorParam()maps values into constructor parameters for immutable types - ForAllMembers — apply a configuration action (e.g.,
Ignore()) to every destination member at once - IncludeMembers — flatten child objects from the source into the destination
- Enum strategies — map enums by name, by value, or case-insensitive via
CreateMap<S,D>().UseEnumMappingStrategy() - AllowNullCollections — preserve
nullcollection properties instead of substituting empty collections - Dictionary-to-object mapping — map
Dictionary<string, object>keys to destination properties by name - Deep cloning —
UseDeepCloning()produces fully independent copies of object graphs - Multiple source mapping —
IncludeSource<T>()merges multiple source types into a single destination - Named mappings — map the same type pair with different configurations using named/tagged profiles
Performance
Benchmarked on .NET 10 (BenchmarkDotNet, Apple M-series, Release mode):
| Scenario | OpenAutoMapper | Mapperly 4.3 | Mapster 10.0 | AutoMapper 16.1 | Hand-written |
|---|---|---|---|---|---|
| Flat DTO (10 props) | 14.16 ns | 14.68 ns | 21.14 ns | 59.82 ns | 13.70 ns |
| Nested DTO | ~19 ns | ~19 ns | ~28 ns | ~85 ns | ~18 ns |
| Collection (100 items) | ~2.1 us | ~2.1 us | ~3.5 us | ~8.2 us | ~2.0 us |
| Startup (cold start) | ~0 ms | ~0 ms | ~5 ms | ~30 ms | N/A |
OpenAutoMapper matches hand-written performance when called via the generated extension method (source.MapToDestDto()). The IMapper interface path adds ~5 ns of dispatch overhead — use direct calls on hot paths.
NativeAOT Performance
Under NativeAOT (where AutoMapper and Mapster cannot run):
| Scenario | OpenAutoMapper | Mapperly | Hand-written |
|---|---|---|---|
| Flat DTO | 14.62 ns (0.94x) | 15.10 ns (0.98x) | 15.48 ns |
| Nested DTO | 19.86 ns (1.10x) | 19.61 ns (1.08x) | 18.10 ns |
Run benchmarks yourself:
dotnet run --project tests/OpenAutoMapper.Benchmarks/ -c Release -- --filter '*Competitive*'
EF Core Projections
OpenAutoMapper generates Expression<Func<S,D>> trees that EF Core translates directly to SQL — no entity materialization, no N+1 queries:
// Profile
public class OrderProfile : Profile
{
public OrderProfile()
{
CreateProjection<Customer, CustomerDto>();
CreateProjection<Address, AddressDto>();
CreateProjection<Order, OrderSummaryDto>();
CreateProjection<OrderLine, OrderLineDto>();
}
}
// Query — generates a single SELECT with only the DTO columns
var summaries = await db.Orders
.Where(o => o.Total > 100m)
.OrderByDescending(o => o.Total)
.ProjectToOrderSummaryDto()
.Take(20)
.ToListAsync();
The generator handles nested objects, nested collections, and convention-based flattening:
// Nested object: Customer.Address → CustomerDto.Address (AddressDto)
var customers = db.Customers.ProjectToCustomerDto().ToList();
// Nested collection: Order.Lines → OrderDetailDto.Lines (List<OrderLineDto>)
var details = db.Orders.ProjectToOrderDetailDto().ToList();
// Flattening by convention: Order.Customer.Name → OrderDto.CustomerName
var orders = db.Orders.ProjectToOrderDto().ToList();
See the EF Core sample for a complete working example, and the Projections guide for the full API.
Projection Limitations
The source generator emits expression trees at compile time. This means:
- Navigation-chain
MapFromexpressions likes.Customer.Nameors.Lines.Countare not translated into the generated expression tree. Use convention-based flattening instead (name the DTO propertyCustomerNameto auto-map fromCustomer.Name). - Runtime-only features (
BeforeMap,AfterMap,Condition,ConstructUsing) cannot be used in projections — they cannot be translated to SQL. - Dictionary properties are not supported in projections and will emit diagnostic OM1042.
What Makes This Different
AutoMapper is a mature, battle-tested library — but it relies heavily on runtime reflection and Expression.Compile(), which makes it incompatible with Native AOT and incurs startup and per-map overhead.
OpenAutoMapper takes a different approach:
- Roslyn source generator — the mapping code is emitted as plain C# during compilation, not at runtime.
- No
Expression.Compile()— expressions are only used for EF CoreProjectTo<T>()projections (which are translated to SQL, not compiled). - Same API surface — if you know AutoMapper, you already know OpenAutoMapper.
Profile,CreateMap,ForMember,Ignore,ReverseMap,IMapper— it is all here. - Diagnostic-first — misconfiguration is caught at compile time with clear OM-series diagnostic messages, not at runtime with cryptic exceptions.
AOT Limitations
OpenAutoMapper is designed for Native AOT from the ground up. However, a few things to be aware of:
- Open generic mappings (
CreateMap(typeof(Wrapper<>), typeof(WrapperDto<>))) are not supported — the generator needs concrete types. - Interface destination types are not supported — the generator must know which concrete type to instantiate.
- Dynamic/runtime map creation is not available — all mappings must be declared at compile time.
- Custom
ITypeConverter<S,D>implementations must be concrete, non-generic classes.
See the AOT Guide for full details.
Migration from AutoMapper
Migrating from AutoMapper is a three-step process:
Step 1: Replace the NuGet package
dotnet remove package AutoMapper
dotnet remove package AutoMapper.Extensions.Microsoft.DependencyInjection
dotnet add package OpenAutoMapper
dotnet add package OpenAutoMapper.DependencyInjection
Step 2: Update the using statements
- using AutoMapper;
+ using OpenAutoMapper;
Step 3: Build and fix diagnostics
dotnet build
The source generator will report any unsupported patterns as OM-series diagnostics. Fix those and you are done.
See the full Migration Guide for a detailed walkthrough.
Documentation
- Getting Started
- Configuration
- Collections
- Projections (EF Core)
- Advanced Topics
- Migration from AutoMapper
- AOT Guide
- Diagnostics Reference
Contributing
Contributions are welcome! Please see the following guidelines:
- Fork the repository and create a feature branch from
main. - Ensure all existing tests pass:
dotnet test - Add tests for any new functionality.
- Run
dotnet build -c Releaseto confirm no warnings-as-errors. - Submit a pull request with a clear description of the change.
Building from Source
git clone https://github.com/OpenAutoMapper/OpenAutoMapper.git
cd OpenAutoMapper
dotnet restore
dotnet build -c Release
dotnet test
License
This project is licensed under the MIT License.
Copyright (c) 2026 D. Ivahno
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 is compatible. 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. |
-
net10.0
- OpenAutoMapper.Core (>= 1.0.0)
-
net8.0
- OpenAutoMapper.Core (>= 1.0.0)
-
net9.0
- OpenAutoMapper.Core (>= 1.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on OpenAutoMapper:
| Package | Downloads |
|---|---|
|
OpenAutoMapper.DependencyInjection
Microsoft.Extensions.DependencyInjection integration for OpenAutoMapper. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 191 | 3/24/2026 |