Why We Built PanoramicData.Mapper
If you use AutoMapper in your .NET projects, two recent changes may affect you:
1. High-Severity Security Vulnerability
GHSA-rvv3-g6hj-g44x is a High severity vulnerability (CVSS 7.5) affecting all AutoMapper versions prior to 16.1.1. It allows denial of service via uncontrolled recursion (CWE-674). While a patch exists in 16.1.1+, upgrading to that version means accepting the new license terms below.
2. License Change from MIT to RPL-1.5
AutoMapper is no longer MIT-licensed. The project, now under “Lucky Penny Software”, uses the Reciprocal Public License 1.5 (RPL-1.5). Under RPL-1.5, if you deploy software using AutoMapper, you must release your entire source code under the same license — or purchase a commercial license. For commercial and closed-source projects, this is a significant change.
Our Response
Rather than accept the licensing implications or suppress the vulnerability warning indefinitely, we built PanoramicData.Mapper — a comprehensive, drop-in replacement for AutoMapper, released under the MIT license.
What Is PanoramicData.Mapper?
PanoramicData.Mapper is a .NET 10 object-to-object mapping library that provides full API compatibility with AutoMapper. It covers 40 distinct API features, each implemented and tested:
- Profile-based configuration — inherit from
Profile, callCreateMap<TSource, TDestination>() - ForMember / Ignore / MapFrom — member-level configuration with lambda expressions
- ForMember (string overload) — member configuration by name
- ForAllMembers — apply configuration to all destination members at once
- BeforeMap / AfterMap — inline lambda and generic
IMappingAction<TSrc, TDst>pre/post-mapping callbacks - ReverseMap —
.ReverseMap()creates the inverse mapping automatically - ConvertUsing — lambda,
ITypeConverter<TSrc, TDst>type, or instance for full-type conversion - ConstructUsing — custom destination construction via lambda
- ForPath —
.ForPath(d => d.Inner.Prop, opt => ...)for deep nested member configuration - ForCtorParam —
.ForCtorParam("name", opt => ...)for constructor parameter mapping - Condition / PreCondition — conditional member mapping (evaluated after/before value resolution)
- NullSubstitute — substitute a default value when the source resolves to null
- Value Resolvers —
IValueResolver<TSrc, TDst, TMember>for custom resolution logic - Mapping Inheritance —
Include,IncludeBase,IncludeAllDerivedfor polymorphic hierarchies - Value Transformers —
.AddTransform<T>(expr)for per-type value transforms - Open Generics —
CreateMap(typeof(Source<>), typeof(Dest<>))for generic type mappings - UseDestinationValue — preserve existing destination property values
- MaxDepth —
.MaxDepth(n)to limit recursive mapping depth - Map to new —
mapper.Map<TDest>(source)creates a new destination - Map to existing —
mapper.Map(source, destination)updates an existing object - Non-generic Map overloads —
mapper.Map(obj, sourceType, destType)andmapper.Map(obj, obj, sourceType, destType) - ProjectTo —
IQueryable<T>.ProjectTo<TDest>(configurationProvider)for EF Core SQL projection - [Ignore] attribute —
PanoramicData.Mapper.Configuration.Annotations.IgnoreAttribute - IgnoreAllPropertiesWithAnInaccessibleSetter — extension method to ignore all destination properties with non-public or absent setters
- Nested mappings — recursive mapping of complex child types and collection properties
- Collection/List/Array mapping —
mapper.Map<List<Dest>>(sourceList)maps collections automatically - Flattening — PascalCase destination property names are split and traversed on the source graph (e.g.
CustomerName→Customer.Name); also matchesGetX()methods - AssertConfigurationIsValid — detects unmapped destination properties at startup
- DI integration —
AddAutoMapper()extension methods forIServiceCollection(assembly scanning, explicit configuration, or both)
Key Design Decisions
| Decision | Rationale |
|---|---|
| MIT license | No licensing surprises for commercial projects |
| .NET 10 only | Modern target, no legacy baggage |
| Reflection-based engine | Simpler implementation, matches AutoMapper runtime behavior |
| Compiled mapper caching | Property assignments compiled once, reused for performance |
| Expression tree projection | ProjectTo builds real expressions that EF Core translates to SQL |
| Codacy-compliant complexity | All methods kept at cyclomatic complexity <= 8 for maintainability |
Test Coverage
The library ships with 90 tests covering all 40 API features, built with xUnit v3 and AwesomeAssertions. Every row in the MASTER PLAN API surface table shows both “Implemented” and “Tested” checkmarks.
CI runs on every push and pull request via GitHub Actions, with code coverage reported to Codacy.
Migration Guide
Migrating from AutoMapper takes three steps:
Step 1 — Swap the NuGet package:
dotnet remove package AutoMapper
dotnet add package PanoramicData.Mapper
Step 2 — Update using directives (ideally in a GlobalUsings.cs):
// Before
global using AutoMapper;
global using AutoMapper.QueryableExtensions;
global using AutoMapper.Configuration.Annotations;
// After
global using PanoramicData.Mapper;
global using PanoramicData.Mapper.QueryableExtensions;
global using PanoramicData.Mapper.Configuration.Annotations;
Step 3 — Build and test. All type names (Profile, IMapper, MapperConfiguration, AddAutoMapper()) remain identical.
That is it. We migrated our own 90+ project solution (10 projects using AutoMapper directly, 84 mapping profiles, ~110 Map call sites) with zero code changes beyond the steps above.
Installation
dotnet add package PanoramicData.Mapper
The package is available on NuGet — current version 10.0.8.
Contributions Welcome
PanoramicData.Mapper is open source under the MIT license. We welcome contributions:
If you are affected by the AutoMapper license change or vulnerability and want a clean, MIT-licensed alternative, give PanoramicData.Mapper a try.