Skip to content

fix: ForMember with Condition but no MapFrom silently drops property#110

Merged
cloud-hai-vo merged 1 commit intomainfrom
fix/forMember-condition-without-mapFrom
Apr 20, 2026
Merged

fix: ForMember with Condition but no MapFrom silently drops property#110
cloud-hai-vo merged 1 commit intomainfrom
fix/forMember-condition-without-mapFrom

Conversation

@cloud-hai-vo
Copy link
Copy Markdown
Contributor

Summary

  • Bug: .ForMember(d => d.Name, opt => opt.Condition(...)) without an explicit MapFrom silently dropped the property instead of mapping it by convention.
  • Root cause: BuildPropertyAction returned null when SourceMemberName was null, but the destination property was already added to processedDestProps — so the convention mapping loop also skipped it, leaving zero actions for that property.
  • Fix: Added a same-name convention fallback in BuildPropertyAction before the final return null. When SourceMemberName is null, EggMapper now looks up the source property by destination property name and builds the action with the full propMap (Condition/PreCondition/NullSubstitute) applied — matching AutoMapper's behavior.

Impact

Any ForMember call that configures only guards (Condition, PreCondition, NullSubstitute, or combinations) without an explicit MapFrom was silently a no-op. With this fix those properties are correctly mapped with the guards applied.

Test plan

  • Condition_without_MapFrom_still_maps_by_convention — property is written when condition is true
  • Condition_without_MapFrom_skips_when_false — existing destination value preserved when condition is false
  • Condition_without_MapFrom_maps_into_existing_destination — regression case: Map(source, destination) path
  • All 358 existing tests still pass

🤖 Generated with Claude Code

…perty

BuildPropertyAction returned null when SourceMemberName was null (no
explicit MapFrom), but the destination property was already marked as
processed — so the convention mapping loop skipped it too, leaving the
property with zero actions and never writing it.

Added a convention fallback: when SourceMemberName is null, look up the
source property by the destination property name and build the action
with the full propMap (Condition/PreCondition/NullSubstitute) applied.
Covers all ForMember-only-guards patterns that AutoMapper handles by
same-name convention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

📊 Benchmark Results

Generated: 2026-04-20 02:43 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.79 ns 3.926 ns 0.215 ns 15.54 ns 15.89 ns 15.94 ns 1.00 0.02 1 0.0048 80 B 1.00
EggMapper 27.28 ns 2.485 ns 0.136 ns 27.16 ns 27.24 ns 27.43 ns 1.73 0.02 2 0.0048 80 B 1.00
AutoMapper 78.38 ns 4.104 ns 0.225 ns 78.17 ns 78.36 ns 78.62 ns 4.96 0.06 3 0.0048 80 B 1.00
Mapster 28.86 ns 2.363 ns 0.130 ns 28.74 ns 28.82 ns 29.00 ns 1.83 0.02 2 0.0048 80 B 1.00
MapperlyMap 16.48 ns 1.915 ns 0.105 ns 16.38 ns 16.47 ns 16.59 ns 1.04 0.01 1 0.0048 80 B 1.00
AgileMapper 326.11 ns 46.532 ns 2.551 ns 324.11 ns 325.23 ns 328.98 ns 20.65 0.28 4 0.0205 344 B 4.30
EggMapperGenerator 16.23 ns 1.407 ns 0.077 ns 16.15 ns 16.23 ns 16.31 ns 1.03 0.01 1 0.0048 80 B 1.00
EggMapperClassMapper 17.47 ns 2.109 ns 0.116 ns 17.36 ns 17.47 ns 17.59 ns 1.11 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 20.80 ns 9.987 ns 0.547 ns 20.19 ns 20.97 ns 21.24 ns 1.00 0.03 1 0.0048 80 B 1.00
EggMap 32.42 ns 5.601 ns 0.307 ns 32.10 ns 32.44 ns 32.71 ns 1.56 0.04 2 0.0048 80 B 1.00
AutoMapper 81.45 ns 3.177 ns 0.174 ns 81.35 ns 81.35 ns 81.66 ns 3.92 0.09 3 0.0048 80 B 1.00
Mapster 34.83 ns 1.968 ns 0.108 ns 34.73 ns 34.82 ns 34.94 ns 1.68 0.04 2 0.0048 80 B 1.00
MapperlyMap 27.12 ns 0.869 ns 0.048 ns 27.08 ns 27.11 ns 27.17 ns 1.30 0.03 2 0.0062 104 B 1.30
AgileMapper 326.94 ns 21.024 ns 1.152 ns 325.77 ns 326.97 ns 328.08 ns 15.73 0.37 4 0.0205 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 63.73 ns 61.401 ns 3.366 ns 61.70 ns 61.88 ns 67.62 ns 1.00 0.06 1 0.0162 272 B 1.00
EggMapper 70.33 ns 4.318 ns 0.237 ns 70.17 ns 70.21 ns 70.60 ns 1.11 0.05 1 0.0162 272 B 1.00
AutoMapper 107.85 ns 0.676 ns 0.037 ns 107.82 ns 107.85 ns 107.89 ns 1.70 0.08 2 0.0162 272 B 1.00
Mapster 76.98 ns 6.577 ns 0.360 ns 76.66 ns 76.92 ns 77.37 ns 1.21 0.05 1 0.0162 272 B 1.00
MapperlyMap 54.69 ns 10.757 ns 0.590 ns 54.34 ns 54.36 ns 55.37 ns 0.86 0.04 1 0.0162 272 B 1.00
AgileMapper 359.49 ns 28.576 ns 1.566 ns 357.73 ns 359.99 ns 360.74 ns 5.65 0.25 3 0.0253 424 B 1.56

🟢 Complex Mapping — nested object + collection

Method Mean Error StdDev Min Median Max Ratio RatioSD Rank Gen0 Allocated Alloc Ratio
Manual 79.23 ns 23.402 ns 1.283 ns 77.81 ns 79.58 ns 80.31 ns 1.00 0.02 1 0.0191 320 B 1.00
EggMapper 105.71 ns 28.864 ns 1.582 ns 103.89 ns 106.46 ns 106.77 ns 1.33 0.03 1 0.0191 320 B 1.00
AutoMapper 152.29 ns 24.336 ns 1.334 ns 150.85 ns 152.54 ns 153.49 ns 1.92 0.03 2 0.0196 328 B 1.02
Mapster 99.24 ns 4.798 ns 0.263 ns 98.94 ns 99.34 ns 99.43 ns 1.25 0.02 1 0.0191 320 B 1.00
MapperlyMap 83.73 ns 17.830 ns 0.977 ns 82.67 ns 83.91 ns 84.60 ns 1.06 0.02 1 0.0191 320 B 1.00
AgileMapper 424.68 ns 27.573 ns 1.511 ns 423.32 ns 424.41 ns 426.31 ns 5.36 0.08 3 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.156 μs 0.7835 μs 0.0429 μs 2.107 μs 2.175 μs 2.186 μs 1.00 0.02 1 0.5264 0.0153 8.65 KB 1.00
EggMapper 1.958 μs 0.4181 μs 0.0229 μs 1.944 μs 1.946 μs 1.985 μs 0.91 0.02 1 0.5264 0.0153 8.65 KB 1.00
AutoMapper 2.586 μs 0.5888 μs 0.0323 μs 2.549 μs 2.602 μs 2.608 μs 1.20 0.02 1 0.6065 0.0191 9.95 KB 1.15
Mapster 2.203 μs 0.4159 μs 0.0228 μs 2.188 μs 2.193 μs 2.229 μs 1.02 0.02 1 0.5264 0.0153 8.65 KB 1.00
MapperlyMap 2.169 μs 0.6345 μs 0.0348 μs 2.136 μs 2.167 μs 2.205 μs 1.01 0.02 1 0.5264 0.0153 8.65 KB 1.00
AgileMapper 2.788 μs 0.4835 μs 0.0265 μs 2.765 μs 2.782 μs 2.817 μs 1.29 0.02 1 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 6.041 μs 3.2172 μs 0.1763 μs 5.902 μs 5.981 μs 6.239 μs 1.00 0.04 1 1.6708 0.0916 27.4 KB 1.00
EggMapper 6.502 μs 1.2983 μs 0.0712 μs 6.429 μs 6.506 μs 6.571 μs 1.08 0.03 1 1.6708 0.0916 27.4 KB 1.00
AutoMapper 7.859 μs 2.7778 μs 0.1523 μs 7.687 μs 7.912 μs 7.977 μs 1.30 0.04 1 1.7548 0.1068 28.7 KB 1.05
Mapster 6.770 μs 1.1219 μs 0.0615 μs 6.706 μs 6.776 μs 6.829 μs 1.12 0.03 1 1.6708 0.0916 27.4 KB 1.00
MapperlyMap 5.967 μs 0.2874 μs 0.0158 μs 5.949 μs 5.976 μs 5.977 μs 0.99 0.02 1 1.6785 0.0992 27.42 KB 1.00
AgileMapper 5.968 μs 0.2413 μs 0.0132 μs 5.954 μs 5.972 μs 5.979 μs 0.99 0.02 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 20.13 μs 0.929 μs 0.051 μs 20.08 μs 20.14 μs 20.18 μs 1.00 0.00 1 5.2490 1.3123 85.99 KB 1.00
EggMapper 20.07 μs 6.875 μs 0.377 μs 19.68 μs 20.10 μs 20.43 μs 1.00 0.02 1 5.2490 1.3123 85.99 KB 1.00
AutoMapper 24.58 μs 18.953 μs 1.039 μs 23.38 μs 25.07 μs 25.28 μs 1.22 0.04 1 5.7678 1.4343 94.34 KB 1.10
Mapster 20.87 μs 2.926 μs 0.160 μs 20.71 μs 20.88 μs 21.03 μs 1.04 0.01 1 5.2490 1.3123 85.99 KB 1.00
MapperlyMap 20.89 μs 3.917 μs 0.215 μs 20.65 μs 20.97 μs 21.05 μs 1.04 0.01 1 5.2490 1.2817 86.02 KB 1.00
AgileMapper 21.99 μs 1.622 μs 0.089 μs 21.89 μs 22.04 μs 22.05 μs 1.09 0.00 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,391.462 μs 1,197.0117 μs 65.6123 μs 1,324.765 μs 1,393.688 μs 1,455.933 μs 1.001 0.06 3 3.9063 1.9531 95.23 KB 1.00
AutoMapperStartup 413.470 μs 639.0771 μs 35.0300 μs 374.842 μs 422.391 μs 443.177 μs 0.298 0.03 2 5.8594 - 103.9 KB 1.09
MapsterStartup 2.488 μs 0.5001 μs 0.0274 μs 2.457 μs 2.500 μs 2.508 μ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.304 ms 2.049 ms 0.1123 ms 1.180 ms 1.336 ms 1.398 ms 1.01 0.11 1 5.8594 - 95.58 KB 1.00
AutoMapper 4.152 ms 11.417 ms 0.6258 ms 3.578 ms 4.059 ms 4.819 ms 3.20 0.49 2 15.6250 7.8125 310.26 KB 3.25
Mapster 4.361 ms 5.436 ms 0.2980 ms 4.027 ms 4.460 ms 4.598 ms 3.36 0.33 2 46.8750 15.6250 769.01 KB 8.05

📝 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 '*'

@cloud-hai-vo cloud-hai-vo merged commit e164138 into main Apr 20, 2026
4 checks passed
@cloud-hai-vo cloud-hai-vo deleted the fix/forMember-condition-without-mapFrom branch April 20, 2026 02:44
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