Repository navigation
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
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.
All pull requests
| PR | Issue | Summary |
|---|---|---|
| #272 | #184 | Relaxed nullability fallback for map method matching |
| #273 | #34 | DictionaryAssignment MappaSettings for dictionary-to-dictionary mapping |
| #274 | #120 | Nested (dot-separated) property paths in mapping attributes |
| #278 | #275 | Flattening/unflattening follow-up |
| #279 | #277 | Strengthen nested-path tests with HasNextSyntaxNode assertions |
| #280 | #276 | Improve method coverage and coverage history SVG |
| #281 | #250 | Before/after map hooks and PR coverage comments |
| #282 | #255 | Richer enum mapping configuration |
| #284 | #245 | IQueryable projection (ProjectTo) |
| #285 | #108 | PreventEnumerableCount MappaSettings option |
| #286 | #134 | Better benchmarks across mappers with CI charts and gist history |
| #291 | #253 | Object factories |
| #292 | #290 | Improve generator tests and assertion rules |
| #294 | #293 | Retry gist updates and make history merges idempotent |
| #296 | #13 | Opt-in compatible map-method reuse for nested mappings |
| #297 | #295 | Update benchmark display |
| #298 | #20 | MappaMustMapTargetProperty attribute |
| #300 | #254 | Private / inaccessible members |
| #302 | #299 | MappaDependencyInjectionAttribute |
| #304 | #303 | Remove unused generated MethodNameElement from IQueryable projections |
| #307 | #249 | Circular reference / reference handling |
| #311 | #306 | Break compile-time mapping cycles by synthesizing private map methods |
| #312 | #308 | Dump successfully generated sources from generator tests |
| #313 | #310 | Require HaveDiagnostics(count) on generator test diagnostic assertions |
| #315 | #309 | Add MAPPA-BENCHMARK-COMPARISON.svg winner table and reorder README benchmarks |
| #316 | #305 | Introduce generic polymorphic type-mapping attributes |
| #317 | #301 | InjectFromAssemblies for cross-assembly DI mapper discovery |
| #318 | #238 | Reduce ReportGenerator CRAP and cyclomatic complexity in Mappa.Generator |
| #330 | #319, #328, #329 | Fix null-class continue, culture typo, and MappaContext docs |
| #332 | #331 | Use Default BenchmarkDotNet job; skip PR benchmarks |
| #333 | #320, #321, #322 | Reference reuse: per-TTarget keys, omit redundant Add, cache manager local |
| #335 | #323, #325 | Narrow syntax providers and honor CancellationToken |
| #336 | #327 | Improve Mappa.Generator branch coverage and comparison chart annotations |
| #337 | #287 | Update outdated NuGet package dependencies |
| #338 | #271 | Release 10.2.0 |