๐บ๏ธ Mappa v10.2.0
A new version of Mappa (v10.2.0) has been released.
Mappa 10.2.0 expands mapping control with reference handling and compile-time cycle breaking, IQueryable projection, nested property paths, dependency-injection registrars, inaccessible-member mapping, object factories, before/after hooks, and richer enum / settings options. Benchmarks, coverage tooling, and generator performance also improve.
Full changelog: v10.1.0โฆv10.2.0
Breaking changes
When upgrading from 10.1.0, see the upgrade guide.
Relaxed nullability matching for existing map methods
- Nested mappings may reuse an existing map method when nullability annotations differ slightly (#184, #272)
- With
#nullable enable, nested mappings may now invoke an existing map method on the mapper, a dependency, or a matching polymorphic method when the underlying types match even if nullability annotations differ. - Exact nullability matches are still preferred; a relaxed match is used only when no exact match exists.
- Supported relaxations: nested needs
TTarget?โ may invoke a method returningTTarget; nested needsTSourceโ may invoke a method acceptingTSource?. - Previously, a nullability mismatch caused the generator to skip the existing method and generate inline mapping (or select another strategy). Review nested mappings that relied on the old behaviour.
- With
Whatโs new
Reference handling
- Circular reference / reference handling (#249, #307) โ Reuse mapped reference-type instances for cycles and shared graphs (
ReferenceReusing), limit runtime nesting (MaxRuntimeDepth), and guard generator discovery (MaxCompileTimeDepth+ compile-time mapping-cycle detection). Not supported onIQueryableprojection. - Per-
TTargetreference reuse and codegen polish (#320, #321, #322, #333) โ Key reuse by source identity andtypeof(TTarget), omit redundantAddReferencePaircalls, and cache the reference-manager local. BreakCompileTimeCycles(#306, #311) โ Optionally break compile-time mapping cycles by synthesizing private map methods; emits MP00078 when a cycle is auto-broken.
New features
IQueryableprojection (ProjectTo-style) (#245, #284) โ Map methods withIQueryable<TSource>โIQueryable<TTarget>emit provider-translatableSelectprojections. Not compatible with Native AOT ([RequiresDynamicCode]).- Nested (dot-separated) property paths (#120, #274, #275, #278) โ Use paths such as
"Address.City"onMappaUseProperty, invoke, constant, context, and ignore attributes for flattening/unflattening. MappaObjectFactory(#253, #291) โ Construct target types via named factory methods instead ofnew.MappaBeforeMap/MappaAfterMap(#250, #281) โ Invoke named hooks immediately before and after the generated root mapping body.MappaMustMapTargetProperty(#20, #298) โ Require listed (or all) non-required target properties to be mapped on the empty-constructor path; otherwise MP00065.- Inaccessible / private members (#254, #300) โ Opt in with
MappaAllowInaccessibleSourceMembers/MappaAllowInaccessibleTargetMembersto read/write private or protected members (and invoke inaccessible constructors) viaUnsafeAccessor. - Generic polymorphic type-mapping attributes (#305, #316) โ Prefer
MappaTypeMapping<TTarget, TSource>andMappaTypeMappingDefault<TDefault>over non-generic forms.
Dependency injection
MappaDependencyInjectionattribute (#299, #302) โ GenerateIServiceCollectionregistration methods for[Mappa]mappers; static DI mappers are skipped with MP00073.InjectFromAssemblies(#301, #317) โ Discover[Mappa]mappers across assemblies for DI registration; Bson and Protobuf DI packages migrate to generated registrars.
MappaSettings and mapping behaviour
DictionaryAssignment(#34, #273) โ Choose indexer orAddinsertion for dictionary-to-dictionary mapping.PreventEnumerableCount(#108, #285) โ AvoidEnumerable.Countfor fixed-size targets when the source length is unknown.CompatibleMapMethod(#13, #296) โ Opt-in reuse of existing map methods when the source is a base/interface type and the method returns a compatible derived type.- Richer enum mapping configuration (#255, #282) โ Configure enumโintegral, enumโstring, and enumโenum pairings via
MappaMapEnumMember,MappaMapEnumIgnore, andMappaMapEnumDefault. - Relaxed nullability fallback for map method matching (#184, #272) โ See Breaking changes.
Projection polish
- Remove unused
{MethodName}ElementfromIQueryableprojections (#303, #304) โ Stop emitting unused local helpers in projection-generated code.
Generator quality and performance
- Narrow syntax providers and honor
CancellationToken(#323, #325, #335) โ Attribute-driven incremental discovery viaForAttributeWithMetadataName, and cooperative cancellation on hot paths. - Reduce ReportGenerator CRAP / cyclomatic complexity (#238, #318) โ Refactor hotspot methods and enforce CRAP/CC maxima in the coverage script.
- Branch coverage improvements (#327, #336) โ Targeted tests for previously uncovered generator branches; comparison-chart annotation polish.
- Bug fixes (#319, #328, #329, #330) โ Null-class continue path, culture typo, and
MappaContextdocumentation corrections.
Benchmarks and CI tooling
- Expanded benchmark suite with SVG charts (#134, #286) โ Broader mapper scenarios, competitor/Mappa percentage charts, and gist/history publishing.
- Benchmark display and comparison charts (#295, #297, #309, #315) โ Bogus-seeded inputs, refined TIME/MEMORY SVGs, and a winner comparison table.
- Default BenchmarkDotNet job; skip PR benchmarks (#331, #332) โ Reduce flakiness and stop running/commenting benchmarks on pull requests.
- Coverage history and PR coverage comments (#250, #276, #280, #281) โ PR coverage comments with drop thresholds and improved coverage-history SVGs.
- Gist update retries (#293, #294) โ Idempotent history merges with retries on gist updates.
Test infrastructure
- Stronger generator syntax assertions (#277, #279, #290, #292) โ Require
HasNextSyntaxNodewalks and full validation afterHaveGeneratedSourceCode(). - Diagnostic assertion rules (#310, #313) โ Require
HaveDiagnostics(n)orNotHaveDiagnostics()on generator test chains. - Dump generated sources from generator tests (#308, #312) โ Persist successful generation dumps for issue reports.
Maintenance
- Update outdated NuGet package dependencies (#287, #337) โ Bump Roslyn, analyzers, test packages, samples, benchmarks, and integration packages.
- Release 10.2.0 (#271, #338) โ Promote version from
10.2.0-alpha.*to stable10.2.0.
Whatโs next
This is a tentative and non exhaustive list of issues that I would like to cover in the upcoming version of Mappa:
Mapping / generator features
- #334 โ Inline mapping via C# interceptors
- #256 โ Collection merge semantics and map into existing collections
- #251 โ Conditional mapping, null substitution, and value transformers
- #248 โ Generic mapping methods
- #247 โ Map into existing target / merge semantics
- #246 โ Reverse / bidirectional mapping
- #283 โ
IQueryableprojection follow-ups - #73 โ Support fields, not only properties
Generator performance
- #326 โ Optimize hot paths (
TypeSymbolExtensions, constructor detection, attribute helpers) - #324 โ Reduce
Collect()fan-out and document incremental host behavior - #314 โ Investigate Mapperly/Mapster benchmark wins and
FastCollectionsbehaviour
Diagnostics and maintainability
- #141 โ Improved debug feature