diff --git a/src/AutoMapper/Configuration/MemberConfigurationExpression.cs b/src/AutoMapper/Configuration/MemberConfigurationExpression.cs index 421d433bd9..8fe59a8af2 100644 --- a/src/AutoMapper/Configuration/MemberConfigurationExpression.cs +++ b/src/AutoMapper/Configuration/MemberConfigurationExpression.cs @@ -222,6 +222,17 @@ public void PreCondition(Func condition) }); } + public void PreCondition(Func condition) + { + PropertyMapActions.Add(pm => + { + Expression> expr = + (src, ctxt) => condition(src, ctxt); + + pm.PreCondition = expr; + }); + } + public void ExplicitExpansion() { PropertyMapActions.Add(pm => pm.ExplicitExpansion = true); diff --git a/src/AutoMapper/IMemberConfigurationExpression.cs b/src/AutoMapper/IMemberConfigurationExpression.cs index 0a176b52db..daf4f5ecf6 100644 --- a/src/AutoMapper/IMemberConfigurationExpression.cs +++ b/src/AutoMapper/IMemberConfigurationExpression.cs @@ -184,6 +184,12 @@ void ResolveUsing(string sourceMemberName) /// Condition to evaluate using the current resolution context void PreCondition(Func condition); + /// + /// Conditionally map this member, evaluated before accessing the source value + /// + /// Condition to evaluate using the source object and the current resolution context + void PreCondition(Func condition); + /// /// Ignore this member for LINQ projections unless explicitly expanded during projection /// diff --git a/src/UnitTests/Bug/ConditionBug.cs b/src/UnitTests/Bug/ConditionBug.cs index 38d00641c1..bf8826d3fa 100644 --- a/src/UnitTests/Bug/ConditionBug.cs +++ b/src/UnitTests/Bug/ConditionBug.cs @@ -199,25 +199,61 @@ public void Should_not_map_value_when_not_null() namespace SourceValueExceptionConditionPropertyBug { using System; + using System.Collections.Generic; using Shouldly; using Xunit; + public enum Property + { + Value1 = 0, + Value2 = 1, + Value3 = 2 + } + public class Source { - public bool Accessed = false; - public int Value + public Dictionary Accessed = new Dictionary + { + {Property.Value1, false}, + {Property.Value2, false}, + {Property.Value3, false} + }; + + public int Value1 { get { - Accessed = true; + Accessed[Property.Value1] = true; return 5; } } + + public int Value2 + { + get + { + Accessed[Property.Value2] = true; + return 10; + } + } + + public int Value3 + { + get + { + Accessed[Property.Value3] = true; + return 15; + } + } } public class Dest { - public int Value { get; set; } + public int Value1 { get; set; } + + public int Value2 { get; set; } + + public int Value3 { get; set; } } public class ConditionTests : NonValidatingSpecBase @@ -225,16 +261,35 @@ public class ConditionTests : NonValidatingSpecBase protected override MapperConfiguration Configuration { get; } = new MapperConfiguration(cfg => { cfg.CreateMap() - .ForMember(d => d.Value, opt => opt.PreCondition((ResolutionContext rc) => false)); + .ForMember(d => d.Value1, opt => opt.PreCondition((Source src) => false)) + .ForMember(d => d.Value2, opt => opt.PreCondition((ResolutionContext rc) => false)) + .ForMember(d => d.Value3, opt => opt.PreCondition((src, rc) => false)); }); [Fact] - public void Should_not_map() + public void Should_not_map_when_precondition_with_source_parameter_is_false() { var source = new Source(); Mapper.Map(source); - source.Accessed.ShouldBeFalse(); + source.Accessed[Property.Value1].ShouldBeFalse(); } + + [Fact] + public void Should_not_map_when_precondition_with_resolutioncontext_parameter_is_false() + { + var source = new Source(); + Mapper.Map(source); + source.Accessed[Property.Value2].ShouldBeFalse(); + } + + [Fact] + public void Should_not_map_when_precondition_with_source_and_resolutioncontext_parameters_is_false() + { + var source = new Source(); + Mapper.Map(source); + source.Accessed[Property.Value3].ShouldBeFalse(); + } + } }