Skip to content

fix: Items propagation in MapInternal + null dict→empty + benchmark cleanup#76

Merged
cloud-hai-vo merged 1 commit intomainfrom
fix/review-items-propagation
Mar 25, 2026
Merged

fix: Items propagation in MapInternal + null dict→empty + benchmark cleanup#76
cloud-hai-vo merged 1 commit intomainfrom
fix/review-items-propagation

Conversation

@cloud-hai-vo
Copy link
Copy Markdown
Contributor

Closes #75

Summary

Fixes found by 3-agent code review of PR #73:

  • Items propagation (HIGH): GetContext(items) now used in MapInternal's base-type walk, interface walk, and collection auto-mapping paths. Previously Items were silently dropped for EF Core proxy types.
  • Null dictionaries → empty: Dictionary<K,V> properties now produce empty dict when source is null, matching collection behavior
  • Benchmark workflow: Closes stale benchmark PRs before creating new one + enables gh pr merge --auto --squash

Test plan

  • 333 unit tests pass on net8.0, net9.0, net10.0
  • All analyzer/generator/class-mapper tests pass

🤖 Generated with Claude Code

…orkflow cleanup

1. Items not passed to GetContext() in MapInternal base-type walk,
   interface walk, and collection auto-mapping paths — found by code review
2. Null dictionaries now produce empty Dictionary<K,V> instead of null
   (consistency with collection null→empty behavior)
3. Benchmark workflow: close stale PRs before creating new one + gh pr merge --auto

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cloud-hai-vo cloud-hai-vo enabled auto-merge (squash) March 25, 2026 06:53
@cloud-hai-vo cloud-hai-vo merged commit 733b006 into main Mar 25, 2026
5 checks passed
@cloud-hai-vo cloud-hai-vo deleted the fix/review-items-propagation branch March 25, 2026 06:54
@github-actions
Copy link
Copy Markdown
Contributor

📊 Benchmark Results

Generated: 2026-03-25 06:59 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 15.52 ns 8.036 ns 0.440 ns 15.19 ns 15.35 ns 16.02 ns 1.00 0.03 1 0.0048 80 B 1.00
EggMapper 27.48 ns 6.228 ns 0.341 ns 27.15 ns 27.47 ns 27.83 ns 1.77 0.05 2 0.0048 80 B 1.00
AutoMapper 82.36 ns 12.758 ns 0.699 ns 81.91 ns 81.99 ns 83.16 ns 5.31 0.13 3 0.0048 80 B 1.00
Mapster 27.81 ns 9.164 ns 0.502 ns 27.31 ns 27.80 ns 28.32 ns 1.79 0.05 2 0.0048 80 B 1.00
MapperlyMap 15.36 ns 9.929 ns 0.544 ns 14.90 ns 15.22 ns 15.96 ns 0.99 0.04 1 0.0048 80 B 1.00
AgileMapper 508.64 ns 107.630 ns 5.900 ns 502.11 ns 510.25 ns 513.57 ns 32.78 0.86 4 0.0200 344 B 4.30
EggMapperGenerator 15.52 ns 1.860 ns 0.102 ns 15.45 ns 15.47 ns 15.64 ns 1.00 0.02 1 0.0048 80 B 1.00
EggMapperClassMapper 15.12 ns 8.444 ns 0.463 ns 14.67 ns 15.09 ns 15.59 ns 0.97 0.04 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 19.24 ns 11.042 ns 0.605 ns 18.55 ns 19.47 ns 19.69 ns 1.00 0.04 1 0.0048 80 B 1.00
EggMap 29.38 ns 10.435 ns 0.572 ns 29.03 ns 29.06 ns 30.04 ns 1.53 0.05 3 0.0048 80 B 1.00
AutoMapper 94.33 ns 16.325 ns 0.895 ns 93.48 ns 94.26 ns 95.26 ns 4.91 0.14 5 0.0048 80 B 1.00
Mapster 36.27 ns 9.919 ns 0.544 ns 35.88 ns 36.05 ns 36.89 ns 1.89 0.06 4 0.0048 80 B 1.00
MapperlyMap 23.94 ns 7.365 ns 0.404 ns 23.51 ns 24.02 ns 24.30 ns 1.25 0.04 2 0.0062 104 B 1.30
AgileMapper 510.68 ns 112.871 ns 6.187 ns 506.62 ns 507.63 ns 517.80 ns 26.57 0.79 6 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 55.79 ns 22.210 ns 1.217 ns 54.38 ns 56.41 ns 56.57 ns 1.00 0.03 1 0.0162 272 B 1.00
EggMapper 65.89 ns 41.507 ns 2.275 ns 63.70 ns 65.72 ns 68.24 ns 1.18 0.04 1 0.0162 272 B 1.00
AutoMapper 119.17 ns 8.505 ns 0.466 ns 118.66 ns 119.27 ns 119.58 ns 2.14 0.04 2 0.0162 272 B 1.00
Mapster 68.37 ns 5.167 ns 0.283 ns 68.06 ns 68.43 ns 68.61 ns 1.23 0.02 1 0.0162 272 B 1.00
MapperlyMap 50.00 ns 32.153 ns 1.762 ns 48.07 ns 50.41 ns 51.52 ns 0.90 0.03 1 0.0162 272 B 1.00
AgileMapper 517.03 ns 79.086 ns 4.335 ns 512.08 ns 518.81 ns 520.18 ns 9.27 0.19 3 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 73.02 ns 61.18 ns 3.354 ns 70.82 ns 71.36 ns 76.88 ns 1.00 0.06 1 0.0191 320 B 1.00
EggMapper 95.53 ns 24.30 ns 1.332 ns 93.99 ns 96.22 ns 96.37 ns 1.31 0.05 2 0.0191 320 B 1.00
AutoMapper 161.95 ns 53.19 ns 2.915 ns 159.04 ns 161.95 ns 164.87 ns 2.22 0.09 3 0.0196 328 B 1.02
Mapster 92.53 ns 47.15 ns 2.584 ns 89.54 ns 94.00 ns 94.04 ns 1.27 0.06 2 0.0191 320 B 1.00
MapperlyMap 74.29 ns 26.77 ns 1.467 ns 72.96 ns 74.04 ns 75.86 ns 1.02 0.04 1 0.0191 320 B 1.00
AgileMapper 581.39 ns 38.83 ns 2.128 ns 579.13 ns 581.68 ns 583.36 ns 7.97 0.31 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 2.100 μs 0.8641 μs 0.0474 μs 2.046 μs 2.119 μs 2.135 μs 1.00 0.03 1 0.5264 0.0153 8.65 KB 1.00
EggMapper 1.854 μs 0.6459 μs 0.0354 μs 1.832 μs 1.836 μs 1.895 μs 0.88 0.02 1 0.5283 0.0172 8.65 KB 1.00
AutoMapper 2.535 μs 0.7886 μs 0.0432 μs 2.486 μs 2.550 μs 2.568 μs 1.21 0.03 2 0.6065 0.0191 9.95 KB 1.15
Mapster 1.910 μs 0.2434 μs 0.0133 μs 1.902 μs 1.903 μs 1.925 μs 0.91 0.02 1 0.5283 0.0172 8.65 KB 1.00
MapperlyMap 1.927 μs 0.7883 μs 0.0432 μs 1.894 μs 1.912 μs 1.976 μs 0.92 0.03 1 0.5283 0.0172 8.65 KB 1.00
AgileMapper 2.563 μs 0.8893 μs 0.0487 μs 2.518 μs 2.556 μs 2.615 μs 1.22 0.03 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.526 μs 3.7738 μs 0.2069 μs 5.398 μs 5.415 μs 5.764 μs 1.00 0.05 1 1.6708 0.0916 27.4 KB 1.00
EggMapper 6.187 μs 1.9524 μs 0.1070 μs 6.069 μs 6.214 μs 6.277 μs 1.12 0.04 1 1.6708 0.0916 27.4 KB 1.00
AutoMapper 7.232 μs 2.8246 μs 0.1548 μs 7.057 μs 7.288 μs 7.352 μs 1.31 0.05 1 1.7548 0.1068 28.7 KB 1.05
Mapster 6.321 μs 2.4116 μs 0.1322 μs 6.174 μs 6.358 μs 6.430 μs 1.14 0.04 1 1.6708 0.0916 27.4 KB 1.00
MapperlyMap 5.481 μs 2.3854 μs 0.1308 μs 5.374 μs 5.441 μs 5.627 μs 0.99 0.04 1 1.6785 0.0992 27.42 KB 1.00
AgileMapper 5.446 μs 0.3478 μs 0.0191 μs 5.425 μs 5.449 μs 5.462 μs 0.99 0.03 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.60 μs 6.639 μs 0.364 μs 17.19 μs 17.76 μs 17.87 μs 1.00 0.03 1 5.2490 1.3123 85.99 KB 1.00
EggMapper 16.67 μs 4.832 μs 0.265 μs 16.48 μs 16.55 μs 16.97 μs 0.95 0.02 1 5.2490 1.3123 85.99 KB 1.00
AutoMapper 21.96 μs 3.726 μs 0.204 μs 21.74 μs 21.99 μs 22.14 μs 1.25 0.02 1 5.7678 1.4343 94.34 KB 1.10
Mapster 17.44 μs 9.951 μs 0.545 μs 16.91 μs 17.41 μs 18.00 μs 0.99 0.03 1 5.2490 1.3123 85.99 KB 1.00
MapperlyMap 18.88 μs 5.307 μs 0.291 μs 18.56 μs 18.93 μs 19.14 μs 1.07 0.02 1 5.2490 1.2817 86.02 KB 1.00
AgileMapper 21.02 μs 6.506 μs 0.357 μs 20.64 μs 21.06 μs 21.35 μs 1.19 0.03 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,295.658 μs 1,468.1429 μs 80.4739 μs 1,242.255 μs 1,256.502 μs 1,388.216 μs 1.002 0.08 3 3.9063 1.9531 94.85 KB 1.00
AutoMapperStartup 423.861 μs 1,259.2774 μs 69.0252 μs 349.290 μs 436.777 μs 485.516 μs 0.328 0.05 2 5.8594 - 104.03 KB 1.10
MapsterStartup 2.560 μs 0.8103 μs 0.0444 μs 2.509 μs 2.584 μs 2.587 μ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.283 ms 1.302 ms 0.0714 ms 1.240 ms 1.244 ms 1.365 ms 1.00 0.07 1 5.8594 - 95.47 KB 1.00
AutoMapper 4.052 ms 9.674 ms 0.5303 ms 3.554 ms 3.994 ms 4.609 ms 3.16 0.39 2 15.6250 7.8125 310.03 KB 3.25
Mapster 4.198 ms 11.940 ms 0.6545 ms 3.554 ms 4.176 ms 4.863 ms 3.28 0.47 2 39.0625 15.6250 754.87 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.

fix: Items not propagated in MapInternal fallback paths + null dictionaries

1 participant