Mappa v10.2.0
From the portfolio: Mappa
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