fix: Items propagation in MapInternal + null dict→empty + benchmark cleanup#76
Merged
cloud-hai-vo merged 1 commit intomainfrom Mar 25, 2026
Merged
Conversation
…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>
Contributor
📊 Benchmark Results
🔵 Flat Mapping — 10-property object
🟡 Flattening — 2 nested objects → 8 flat properties
🟣 Deep Mapping — 2 nested address objects
🟢 Complex Mapping — nested object + collection
🟠 Collection — 100-item
|
| 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) addsMin,Median, andMaxcolumns. - 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 '*'
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #75
Summary
Fixes found by 3-agent code review of PR #73:
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.Dictionary<K,V>properties now produce empty dict when source is null, matching collection behaviorgh pr merge --auto --squashTest plan
🤖 Generated with Claude Code