Skip to content

Feat/same type auto map#91

Merged
cloud-hai-vo merged 2 commits intomainfrom
feat/same-type-auto-map
Mar 27, 2026
Merged

Feat/same type auto map#91
cloud-hai-vo merged 2 commits intomainfrom
feat/same-type-auto-map

Conversation

@cloud-hai-vo
Copy link
Copy Markdown
Contributor

Summary

Changes

Checklist

  • Tests pass (dotnet test --configuration Release)
  • No extra allocations introduced (if touching core mapping)
  • Benchmarks checked (if touching performance-critical code)

cloud-hai-vo and others added 2 commits March 27, 2026 20:55
.NET boxes Nullable<T> as T, stripping the wrapper. When
CreateMap<T?, U>() is registered, the TypePair key uses Nullable<T>
but source.GetType() at runtime returns T — lookup fails silently.

Added fallback: after initial FrozenMaps miss, try Nullable<sourceType>
as the source type in the key. Fixes ProductTrack.TrackMedia mapping
where source is (FlacFile, OriginFile)? nullable tuple.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Guard against Nullable<Nullable<T>> crash in MapInternal — check
   Nullable.GetUnderlyingType before MakeGenericType

2. Add nullable boxing fallback to MapSlow — was only in MapInternal,
   Map<T?, U>() via generic path would miss the registered mapping

3. Add nullable boxing fallback to FindElementDelegate — collection
   elements with boxed nullable value types now resolve correctly

4. Same-type auto-map: skip abstract classes, interfaces, and types
   without parameterless constructors — fail fast instead of runtime
   MissingMethodException

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cloud-hai-vo cloud-hai-vo force-pushed the feat/same-type-auto-map branch from 52ce491 to 06116b8 Compare March 27, 2026 13:56
@cloud-hai-vo cloud-hai-vo enabled auto-merge (squash) March 27, 2026 13:56
@cloud-hai-vo cloud-hai-vo merged commit 3fe465e into main Mar 27, 2026
5 checks passed
@cloud-hai-vo cloud-hai-vo deleted the feat/same-type-auto-map branch March 27, 2026 13:57
@github-actions
Copy link
Copy Markdown
Contributor

📊 Benchmark Results

Generated: 2026-03-27 14:03 UTC  ·  Download full artifacts

Column guide:
Mean = average execution time  ·  Error = half of 99.9 % confidence interval  ·  StdDev = standard deviation  ·  Min / Median / Max = statistical range  ·  Ratio = vs Manual baseline (lower = closer to hand-written speed)  ·  RatioSD = ratio std dev  ·  Rank = 1 is fastest  ·  Gen0/1/2 = GC collections per 1 000 ops  ·  Allocated = managed heap per operation  ·  Alloc Ratio = allocation ratio vs baseline

🔵 Flat Mapping — 10-property object

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Allocated Alloc Ratio
Manual 14.86 ns 1.833 ns 0.100 ns 14.79 ns 14.82 ns 14.97 ns 1.00 0.01 1 0.0048 80 B 1.00
EggMapper 27.29 ns 2.347 ns 0.129 ns 27.20 ns 27.22 ns 27.44 ns 1.84 0.01 2 0.0048 80 B 1.00
AutoMapper 82.13 ns 2.269 ns 0.124 ns 82.01 ns 82.14 ns 82.26 ns 5.53 0.03 3 0.0048 80 B 1.00
Mapster 28.02 ns 1.092 ns 0.060 ns 27.96 ns 28.02 ns 28.08 ns 1.89 0.01 2 0.0048 80 B 1.00
MapperlyMap 14.71 ns 3.645 ns 0.200 ns 14.51 ns 14.70 ns 14.91 ns 0.99 0.01 1 0.0048 80 B 1.00
AgileMapper 494.61 ns 26.243 ns 1.438 ns 492.97 ns 495.21 ns 495.66 ns 33.29 0.21 4 0.0200 344 B 4.30
EggMapperGenerator 15.26 ns 2.716 ns 0.149 ns 15.11 ns 15.28 ns 15.41 ns 1.03 0.01 1 0.0048 80 B 1.00
EggMapperClassMapper 15.09 ns 0.924 ns 0.051 ns 15.04 ns 15.10 ns 15.14 ns 1.02 0.01 1 0.0048 80 B 1.00

🟡 Flattening — 2 nested objects → 8 flat properties

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Allocated Alloc Ratio
Manual 18.82 ns 2.717 ns 0.149 ns 18.65 ns 18.91 ns 18.91 ns 1.00 0.01 1 0.0048 80 B 1.00
EggMap 29.98 ns 4.882 ns 0.268 ns 29.75 ns 29.93 ns 30.27 ns 1.59 0.02 3 0.0048 80 B 1.00
AutoMapper 95.79 ns 2.784 ns 0.153 ns 95.61 ns 95.87 ns 95.88 ns 5.09 0.04 4 0.0048 80 B 1.00
Mapster 35.85 ns 5.402 ns 0.296 ns 35.61 ns 35.75 ns 36.18 ns 1.90 0.02 3 0.0048 80 B 1.00
MapperlyMap 23.15 ns 4.359 ns 0.239 ns 22.91 ns 23.16 ns 23.39 ns 1.23 0.01 2 0.0062 104 B 1.30
AgileMapper 516.19 ns 70.530 ns 3.866 ns 512.31 ns 516.22 ns 520.04 ns 27.42 0.26 5 0.0200 344 B 4.30

🟣 Deep Mapping — 2 nested address objects

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Allocated Alloc Ratio
Manual 52.94 ns 5.767 ns 0.316 ns 52.69 ns 52.83 ns 53.29 ns 1.00 0.01 1 0.0162 272 B 1.00
EggMapper 64.50 ns 33.049 ns 1.812 ns 63.07 ns 63.88 ns 66.53 ns 1.22 0.03 2 0.0162 272 B 1.00
AutoMapper 117.89 ns 12.291 ns 0.674 ns 117.13 ns 118.14 ns 118.40 ns 2.23 0.02 3 0.0162 272 B 1.00
Mapster 66.72 ns 9.688 ns 0.531 ns 66.11 ns 67.02 ns 67.03 ns 1.26 0.01 2 0.0162 272 B 1.00
MapperlyMap 49.53 ns 20.675 ns 1.133 ns 48.85 ns 48.90 ns 50.84 ns 0.94 0.02 1 0.0162 272 B 1.00
AgileMapper 510.89 ns 36.594 ns 2.006 ns 508.72 ns 511.27 ns 512.68 ns 9.65 0.06 4 0.0248 424 B 1.56

🟢 Complex Mapping — nested object + collection

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Allocated Alloc Ratio
Manual 68.97 ns 9.998 ns 0.548 ns 68.34 ns 69.25 ns 69.32 ns 1.00 0.01 1 0.0191 320 B 1.00
EggMapper 90.01 ns 21.113 ns 1.157 ns 88.69 ns 90.54 ns 90.82 ns 1.31 0.02 2 0.0191 320 B 1.00
AutoMapper 159.11 ns 5.298 ns 0.290 ns 158.90 ns 158.98 ns 159.44 ns 2.31 0.02 3 0.0196 328 B 1.02
Mapster 86.93 ns 13.574 ns 0.744 ns 86.25 ns 86.80 ns 87.72 ns 1.26 0.01 2 0.0191 320 B 1.00
MapperlyMap 71.22 ns 11.361 ns 0.623 ns 70.50 ns 71.55 ns 71.60 ns 1.03 0.01 1 0.0191 320 B 1.00
AgileMapper 564.75 ns 52.900 ns 2.900 ns 562.62 ns 563.58 ns 568.05 ns 8.19 0.07 4 0.0315 528 B 1.65

🟠 Collection — 100-item List<T>

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Gen1 Allocated Alloc Ratio
Manual 1.692 μs 0.4062 μs 0.0223 μs 1.671 μs 1.690 μs 1.716 μs 1.00 0.02 1 0.5283 0.0172 8.65 KB 1.00
EggMapper 1.786 μs 0.3209 μs 0.0176 μs 1.766 μs 1.791 μs 1.800 μs 1.06 0.01 1 0.5283 0.0172 8.65 KB 1.00
AutoMapper 2.475 μs 1.5995 μs 0.0877 μs 2.378 μs 2.496 μs 2.550 μs 1.46 0.05 2 0.6065 0.0191 9.95 KB 1.15
Mapster 1.771 μs 0.7654 μs 0.0420 μs 1.727 μs 1.777 μs 1.810 μs 1.05 0.02 1 0.5283 0.0172 8.65 KB 1.00
MapperlyMap 1.765 μs 0.2068 μs 0.0113 μs 1.755 μs 1.763 μs 1.777 μs 1.04 0.01 1 0.5283 0.0172 8.65 KB 1.00
AgileMapper 2.501 μs 0.3376 μs 0.0185 μs 2.485 μs 2.498 μs 2.521 μs 1.48 0.02 2 0.5417 0.0153 8.91 KB 1.03

🟠 Collection — 100-item List<T>

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Gen1 Allocated Alloc Ratio
Manual 5.174 μs 0.1965 μs 0.0108 μs 5.164 μs 5.174 μs 5.185 μs 1.00 0.00 1 1.6708 0.0916 27.4 KB 1.00
EggMapper 5.657 μs 0.7458 μs 0.0409 μs 5.615 μs 5.659 μs 5.696 μs 1.09 0.01 1 1.6708 0.0916 27.4 KB 1.00
AutoMapper 6.517 μs 0.8210 μs 0.0450 μs 6.469 μs 6.524 μs 6.558 μs 1.26 0.01 1 1.7548 0.1068 28.7 KB 1.05
Mapster 5.770 μs 1.7767 μs 0.0974 μs 5.664 μs 5.788 μs 5.857 μs 1.12 0.02 1 1.6708 0.0916 27.4 KB 1.00
MapperlyMap 5.359 μs 1.4822 μs 0.0812 μs 5.279 μs 5.358 μs 5.441 μs 1.04 0.01 1 1.6785 0.0992 27.42 KB 1.00
AgileMapper 5.225 μs 0.8870 μs 0.0486 μs 5.190 μs 5.206 μs 5.281 μs 1.01 0.01 1 1.0223 0.0610 16.72 KB 0.61

🟠 Collection — 100-item List<T>

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Gen1 Allocated Alloc Ratio
Manual 17.07 μs 12.899 μs 0.707 μs 16.41 μs 16.98 μs 17.81 μs 1.00 0.05 1 5.2490 1.3123 85.99 KB 1.00
EggMapper 17.15 μs 3.187 μs 0.175 μs 17.01 μs 17.10 μs 17.35 μs 1.01 0.04 1 5.2490 1.3123 85.99 KB 1.00
AutoMapper 21.74 μs 10.308 μs 0.565 μs 21.41 μs 21.42 μs 22.39 μs 1.28 0.05 1 5.7678 1.4343 94.34 KB 1.10
Mapster 17.55 μs 1.984 μs 0.109 μs 17.43 μs 17.56 μs 17.65 μs 1.03 0.04 1 5.2490 1.3123 85.99 KB 1.00
MapperlyMap 18.59 μs 6.689 μs 0.367 μs 18.27 μs 18.52 μs 18.99 μs 1.09 0.04 1 5.2490 1.2817 86.02 KB 1.00
AgileMapper 20.05 μs 2.982 μs 0.163 μs 19.88 μs 20.06 μs 20.21 μs 1.18 0.04 1 5.2795 1.3123 86.25 KB 1.00

⚪ Startup / Configuration time

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Gen1 Allocated Alloc Ratio
EggMapperStartup 1,284.509 μs 2,022.2581 μs 110.8468 μs 1,160.353 μs 1,319.643 μs 1,373.532 μs 1.005 0.11 3 3.9063 1.9531 95.29 KB 1.00
AutoMapperStartup 395.170 μs 718.5274 μs 39.3849 μs 353.748 μs 399.621 μs 432.140 μs 0.309 0.04 2 5.8594 - 104.52 KB 1.10
MapsterStartup 2.497 μs 0.4115 μs 0.0226 μs 2.478 μs 2.490 μs 2.522 μs 0.002 0.00 1 0.7019 0.0267 11.51 KB 0.12

EggMapper.Benchmarks.ColdStartBenchmark-report-github

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Gen1 Allocated Alloc Ratio
EggMapper 1.319 ms 0.8678 ms 0.0476 ms 1.264 ms 1.344 ms 1.349 ms 1.00 0.04 1 5.8594 3.9063 95.8 KB 1.00
AutoMapper 4.037 ms 9.7928 ms 0.5368 ms 3.536 ms 3.970 ms 4.604 ms 3.06 0.37 2 15.6250 7.8125 309.85 KB 3.23
Mapster 4.318 ms 9.9590 ms 0.5459 ms 3.714 ms 4.461 ms 4.777 ms 3.28 0.37 2 39.0625 15.6250 757.88 KB 7.91

📝 Notes
  • Each benchmark class is decorated with [MemoryDiagnoser] and [RankColumn].
  • The global config (see src/EggMapper.Benchmarks/Program.cs) adds Min, Median, and Max columns.
  • Manual is the hand-written baseline (ratio = 1.00). A ratio < 1 means faster than manual.
  • Benchmarks run on GitHub-hosted runners — absolute times may vary between runs; focus on Ratio for comparisons.
  • To reproduce locally:
    cd src/EggMapper.Benchmarks
    dotnet run --configuration Release -- --filter '*'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant