A comprehensive, API-compatible replacement for AutoMapper. Consuming projects need to:
- Swap the NuGet reference from
AutoMapper/AutoMapper.Extensions.Microsoft.DependencyInjectiontoPanoramicData.Mapper - Change
using AutoMapper;tousing PanoramicData.Mapper;Hint: use aGlobalUsings.csto permit testing the swap-out in one place.
All types live in PanoramicData.Mapper namespaces:
PanoramicData.Mapper— IMapper, Mapper, MapperConfiguration, Profile, IMappingExpression, IValueResolver, ITypeConverter, IMappingAction, ResolutionContext, etc.PanoramicData.Mapper.Configuration.Annotations—[Ignore]attributePanoramicData.Mapper.QueryableExtensions—ProjectTo<T>()extensionPanoramicData.Mapper.Internal— TypeMap, PropertyMapping (internal engine)Microsoft.Extensions.DependencyInjection—AddAutoMapper()extensions (unchanged, standard DI namespace)
- Library builds clean (
TreatWarningsAsErrorsenabled) - 90 tests — all passing
- Published to NuGet.org as v10.0.7
- Codacy grade: A — all complexity and best-practice issues resolved
- GitHub Actions CI: build + test + coverage on push/PR; CodeQL weekly + on push/PR
- GitHub issue #1 (IgnoreAllPropertiesWithAnInaccessibleSetter) — closed
- No open issues
- Fix failing test
- Create missing test files (ProfileRegistration, AssertConfigurationIsValid, DependencyInjection)
- Change namespaces to PanoramicData.Mapper
- Create Publish.ps1
- Add TreatWarningsAsErrors
- Add performance tests
- Git commit and push
- Set
GeneratePackageOnBuildtotruein csproj - Verified
dotnet packproduces a valid .nupkg + .snupkg - Published to NuGet.org via
.\Publish.ps1
Reviewed against https://docs.automapper.org/en/stable/ and implemented:
- Nested Mappings — recursive mapping of complex child types; collection-typed properties (e.g.,
List<ChildSource>?List<ChildDest>) - List/Array mapping —
mapper.Map<List<Dest>>(sourceList)forList<T>,T[], and interface types - Flattening — PascalCase splitting +
GetX()method matching + deep nesting (e.g.,OrderItemName?Order.Item.Name)
- Codacy integration — badge in README, grade A
- Code coverage — coverlet collects Cobertura XML; Codacy coverage reporter uploads on main push
- GitHub Actions CI — build + test + coverage on push/PR; CodeQL weekly + on push/PR
All features previously marked "out of scope" are now implemented and tested:
- ReverseMap —
.ReverseMap()creates the inverse mapping automatically - BeforeMap —
.BeforeMap((src, dst) => ...)pre-mapping callback (AfterMap already implemented) - Conditional Mapping —
.Condition()and.PreCondition()on ForMember - Null Substitution —
.NullSubstitute(value)on ForMember - Value Resolvers —
IValueResolver<TSrc, TDst, TMember>for custom resolution logic - Type Converters —
ITypeConverter<TSrc, TDst>andConvertUsing()for full-type conversion - Construction —
ConstructUsing()for custom destination construction - ForPath —
.ForPath(d => d.Inner.Prop, opt => ...)for deep member configuration - ForCtorParam —
.ForCtorParam("paramName", opt => ...)for constructor parameter mapping - Mapping Inheritance —
Include,IncludeBase,IncludeAllDerivedfor polymorphic hierarchies - Value Transformers — Global/profile-level value transforms
- Open Generics —
CreateMap(typeof(Source<>), typeof(Dest<>)) - UseDestinationValue —
.UseDestinationValue()to preserve existing destination collection instances - MaxDepth —
.MaxDepth(n)to limit recursive mapping depth - Async mapping — deferred (no async I/O in mapping pipeline; all mapping is synchronous by design)
- Refactored TypeMap.cs: extracted 10+ helper methods to reduce cyclomatic complexity (CC ? 8 per method)
- Refactored MapperConfiguration.cs: extracted
TryResolveInheritedMap,CreateAndCacheDerivedMap,TryResolveOpenGenericMap - Fixed 3 unused parameter warnings in test interface implementations
- IgnoreAllPropertiesWithAnInaccessibleSetter — extension method on
IMappingExpression<TSource, TDestination>to ignore all destination properties with non-public or absent setters (GitHub issue #1)
| AutoMapper Feature | Implemented | Tested |
|---|---|---|
CreateMap<S,D>() |
? | ? |
.ForMember(expr, opt => opt.Ignore()) |
? | ? |
.ForMember(expr, opt => opt.MapFrom(...)) |
? | ? |
.ForMember(string, opt => ...) |
? | ? |
.AfterMap((src, dst) => ...) |
? | ? |
.AfterMap<TMappingAction>() |
? | ? |
.BeforeMap((src, dst) => ...) |
? | ? |
.BeforeMap<TMappingAction>() |
? | ? |
.ForAllMembers(opt => opt.Ignore()) |
? | ? |
.ReverseMap() |
? | ? |
.ConvertUsing(lambda / ITypeConverter) |
? | ? |
.ConstructUsing(lambda) |
? | ? |
.ForPath(d => d.Inner.Prop, opt => ...) |
? | ? |
.ForCtorParam("name", opt => ...) |
? | ? |
.Condition() / .PreCondition() |
? | ? |
.NullSubstitute(value) |
? | ? |
.MapFrom<IValueResolver>() |
? | ? |
.Include<TDerived>() |
? | ? |
.IncludeBase<TBase>() |
? | ? |
.IncludeAllDerived() |
? | ? |
.MaxDepth(n) |
? | ? |
.AddTransform<T>(expr) |
? | ? |
.UseDestinationValue() |
? | ? |
CreateMap(typeof(S<>), typeof(D<>)) |
? | ? |
IMapper.Map<T>(object) |
? | ? |
IMapper.Map<S,D>(src) |
? | ? |
IMapper.Map<S,D>(src, dest) |
? | ? |
IMapper.Map(obj, Type, Type) |
? | ? |
IMapper.Map(obj, obj, Type, Type) |
? | ? |
ProjectTo<T>(configProvider) |
? | ? |
[Ignore] attribute |
? | ? |
AddAutoMapper(typeof(...)) |
? | ? |
AddAutoMapper(Assembly[]) |
? | ? |
AddAutoMapper(Action<IMapperConfigurationExpression>) |
? | ? |
AssertConfigurationIsValid() |
? | ? |
IConfigurationProvider (for ProjectTo) |
? | ? |
| Nested mappings (complex child types) | ? | ? |
| Collection/List/Array mapping | ? | ? |
| Flattening (PascalCase split + GetX()) | ? | ? |
IgnoreAllPropertiesWithAnInaccessibleSetter() |
? | ? |
- All types in
PanoramicData.Mappernamespace — consuming projects swap the NuGet reference and updateusingdirectives - net10.0 only — no netstandard2.0 (simplified for modern consumers)
- Reflection-based engine (not source generators) — simpler, matches AutoMapper runtime behavior
- Compiled mapper caching — TypeMap compiles property assignments once, reuses for subsequent maps
- Expression tree projection — ProjectTo builds real Expression trees that EF Core can translate to SQL
- nbgv versioning — consistent with all PanoramicData repos
- xunit.v3 + AwesomeAssertions — consistent with PanoramicData test conventions
- Codacy-compliant complexity — all methods ? CC 8; helpers extracted for readability