Skip to content

Commit f02948e

Browse files
authored
Merge pull request #20 from agileobjects/MapNullConfiguration
Map null configuration
2 parents 0516392 + ee4870b commit f02948e

File tree

16 files changed

+405
-41
lines changed

16 files changed

+405
-41
lines changed

AgileMapper.Net40/AgileMapper.Net40.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
<Reference Include="AgileObjects.NetStandardPolyfills, Version=0.2.1.0, Culture=neutral, PublicKeyToken=06131ac1c008ad4e, processorArchitecture=MSIL">
3737
<HintPath>..\packages\AgileObjects.NetStandardPolyfills.0.2.1\lib\net40\AgileObjects.NetStandardPolyfills.dll</HintPath>
3838
</Reference>
39-
<Reference Include="AgileObjects.ReadableExpressions, Version=1.8.5.0, Culture=neutral, PublicKeyToken=9f54ad81db69da8e, processorArchitecture=MSIL">
40-
<HintPath>..\packages\AgileObjects.ReadableExpressions.1.8.5\lib\net40\AgileObjects.ReadableExpressions.dll</HintPath>
39+
<Reference Include="AgileObjects.ReadableExpressions, Version=1.8.6.0, Culture=neutral, PublicKeyToken=9f54ad81db69da8e, processorArchitecture=MSIL">
40+
<HintPath>..\packages\AgileObjects.ReadableExpressions.1.8.6\lib\net40\AgileObjects.ReadableExpressions.dll</HintPath>
4141
</Reference>
4242
<Reference Include="System" />
4343
<Reference Include="System.Core" />

AgileMapper.Net40/packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
33
<package id="AgileObjects.NetStandardPolyfills" version="0.2.1" targetFramework="net40" />
4-
<package id="AgileObjects.ReadableExpressions" version="1.8.5" targetFramework="net40" />
4+
<package id="AgileObjects.ReadableExpressions" version="1.8.6" targetFramework="net40" />
55
</packages>

AgileMapper.PerformanceTester/AgileMapper.PerformanceTester.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
<Reference Include="AgileObjects.NetStandardPolyfills, Version=0.2.1.0, Culture=neutral, PublicKeyToken=06131ac1c008ad4e, processorArchitecture=MSIL">
3737
<HintPath>..\packages\AgileObjects.NetStandardPolyfills.0.2.1\lib\net40\AgileObjects.NetStandardPolyfills.dll</HintPath>
3838
</Reference>
39-
<Reference Include="AgileObjects.ReadableExpressions, Version=1.8.5.0, Culture=neutral, PublicKeyToken=9f54ad81db69da8e, processorArchitecture=MSIL">
40-
<HintPath>..\packages\AgileObjects.ReadableExpressions.1.8.5\lib\net40\AgileObjects.ReadableExpressions.dll</HintPath>
39+
<Reference Include="AgileObjects.ReadableExpressions, Version=1.8.6.0, Culture=neutral, PublicKeyToken=9f54ad81db69da8e, processorArchitecture=MSIL">
40+
<HintPath>..\packages\AgileObjects.ReadableExpressions.1.8.6\lib\net40\AgileObjects.ReadableExpressions.dll</HintPath>
4141
</Reference>
4242
<Reference Include="AutoMapper, Version=5.2.0.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
4343
<HintPath>..\packages\AutoMapper.5.2.0\lib\net45\AutoMapper.dll</HintPath>

AgileMapper.PerformanceTester/packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
33
<package id="AgileObjects.NetStandardPolyfills" version="0.2.1" targetFramework="net452" />
4-
<package id="AgileObjects.ReadableExpressions" version="1.8.5" targetFramework="net452" />
4+
<package id="AgileObjects.ReadableExpressions" version="1.8.6" targetFramework="net452" />
55
<package id="AutoMapper" version="5.2.0" targetFramework="net452" />
66
<package id="Expressmapper" version="1.8.3" targetFramework="net452" />
77
<package id="Mapster" version="2.6.1" targetFramework="net452" />

AgileMapper.UnitTests/AgileMapper.UnitTests.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
<Reference Include="AgileObjects.NetStandardPolyfills, Version=0.2.1.0, Culture=neutral, PublicKeyToken=06131ac1c008ad4e, processorArchitecture=MSIL">
4040
<HintPath>..\packages\AgileObjects.NetStandardPolyfills.0.2.1\lib\net40\AgileObjects.NetStandardPolyfills.dll</HintPath>
4141
</Reference>
42-
<Reference Include="AgileObjects.ReadableExpressions, Version=1.8.5.0, Culture=neutral, PublicKeyToken=9f54ad81db69da8e, processorArchitecture=MSIL">
43-
<HintPath>..\packages\AgileObjects.ReadableExpressions.1.8.5\lib\net40\AgileObjects.ReadableExpressions.dll</HintPath>
42+
<Reference Include="AgileObjects.ReadableExpressions, Version=1.8.6.0, Culture=neutral, PublicKeyToken=9f54ad81db69da8e, processorArchitecture=MSIL">
43+
<HintPath>..\packages\AgileObjects.ReadableExpressions.1.8.6\lib\net40\AgileObjects.ReadableExpressions.dll</HintPath>
4444
</Reference>
4545
<Reference Include="Shouldly, Version=2.8.2.0, Culture=neutral, PublicKeyToken=6042cbcb05cbc941, processorArchitecture=MSIL">
4646
<HintPath>..\packages\Shouldly.2.8.2\lib\net451\Shouldly.dll</HintPath>
@@ -75,6 +75,7 @@
7575
<Link>VersionInfo.cs</Link>
7676
</Compile>
7777
<Compile Include="Configuration\WhenConfiguringDerivedTypesIncorrectly.cs" />
78+
<Compile Include="Configuration\WhenMappingToNull.cs" />
7879
<Compile Include="Dictionaries\Configuration\WhenConfiguringSourceDictionaryMapping.cs" />
7980
<Compile Include="Dictionaries\Configuration\WhenConfiguringDictionaryMappingIncorrectly.cs" />
8081
<Compile Include="Dictionaries\Configuration\WhenConfiguringNestedDictionaryMapping.cs" />
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
namespace AgileObjects.AgileMapper.UnitTests.Configuration
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using AgileMapper.Configuration;
7+
using Shouldly;
8+
using TestClasses;
9+
using Xunit;
10+
11+
public class WhenMappingToNull
12+
{
13+
[Fact]
14+
public void ShouldApplyAUserConfiguredCondition()
15+
{
16+
using (var mapper = Mapper.CreateNew())
17+
{
18+
mapper.WhenMapping
19+
.To<Address>()
20+
.If((o, a) => string.IsNullOrWhiteSpace(a.Line1))
21+
.MapToNull();
22+
23+
var source = new CustomerViewModel { Name = "Bob" };
24+
var result = mapper.Map(source).ToANew<Customer>();
25+
26+
result.Address.ShouldBeNull();
27+
}
28+
}
29+
30+
[Fact]
31+
public void ShouldRestrictAConfiguredMapToNullConditionBySourceType()
32+
{
33+
using (var mapper = Mapper.CreateNew())
34+
{
35+
mapper.WhenMapping
36+
.From<CustomerViewModel>()
37+
.To<Address>()
38+
.If((o, a) => a.Line1 == "Null!")
39+
.MapToNull();
40+
41+
var nonMatchingSource = new Customer
42+
{
43+
Name = "Jen",
44+
Address = new Address { Line1 = "Null!" }
45+
};
46+
var nonMatchingResult = mapper.Map(nonMatchingSource).ToANew<Customer>();
47+
48+
nonMatchingResult.Address.ShouldNotBeNull();
49+
nonMatchingResult.Address.Line1.ShouldBe("Null!");
50+
51+
var matchingSource = new CustomerViewModel
52+
{
53+
Name = "Frank",
54+
AddressLine1 = "Null!"
55+
};
56+
var matchingResult = mapper.Map(matchingSource).ToANew<Customer>();
57+
58+
matchingResult.Address.ShouldBeNull();
59+
}
60+
}
61+
62+
[Fact]
63+
public void ShouldOverwriteAPropertyToNull()
64+
{
65+
using (var mapper = Mapper.CreateNew())
66+
{
67+
mapper.WhenMapping
68+
.Over<Address>()
69+
.If(ctx => ctx.Target.Line1 == null)
70+
.MapToNull();
71+
72+
var nonMatchingSource = new CustomerViewModel
73+
{
74+
Id = Guid.NewGuid(),
75+
AddressLine1 = "Places!"
76+
};
77+
var target = new Customer { Address = new Address { Line1 = "Home!" } };
78+
79+
mapper.Map(nonMatchingSource).Over(target);
80+
81+
target.Address.ShouldNotBeNull();
82+
target.Address.Line1.ShouldBe("Places!");
83+
84+
var matchingSource = new CustomerViewModel { Id = Guid.NewGuid() };
85+
86+
mapper.Map(matchingSource).Over(target);
87+
88+
target.Address.ShouldBeNull();
89+
90+
var nullAddressTarget = new Customer();
91+
92+
mapper.Map(matchingSource).Over(nullAddressTarget);
93+
94+
target.Address.ShouldBeNull();
95+
}
96+
}
97+
98+
[Fact]
99+
public void ShouldApplyConfiguredConditionsToDerivedTypes()
100+
{
101+
using (var mapper = Mapper.CreateNew())
102+
{
103+
mapper.WhenMapping
104+
.To<Product>()
105+
.If((o, p) => p.Price.Equals(0))
106+
.MapToNull();
107+
108+
mapper.WhenMapping
109+
.To<MegaProduct>()
110+
.If((o, p) => p.HowMega == 0)
111+
.MapToNull();
112+
113+
var nonMatchingProductSource = new { Price = 123 };
114+
var nonMatchingProductResult = mapper.Map(nonMatchingProductSource).ToANew<Product>();
115+
116+
nonMatchingProductResult.Price.ShouldBe(123);
117+
118+
var matchingProductSource = new { Price = 0 };
119+
var matchingProductResult = mapper.Map(matchingProductSource).ToANew<Product>();
120+
121+
matchingProductResult.ShouldBeNull();
122+
123+
var nonMatchingMegaProductSource = new { HowMega = 0.99 };
124+
var nonMatchingMegaProductResult = mapper.Map(nonMatchingMegaProductSource).ToANew<MegaProduct>();
125+
126+
nonMatchingMegaProductResult.HowMega.ShouldBe(0.99);
127+
128+
var matchingMegaProductSource = new { HowMega = 0.00 };
129+
var matchingMegaProductResult = mapper.Map(matchingMegaProductSource).ToANew<MegaProduct>();
130+
131+
matchingMegaProductResult.ShouldBeNull();
132+
}
133+
}
134+
135+
[Fact]
136+
public void ShouldNotMapCollectionElementsToNull()
137+
{
138+
using (var mapper = Mapper.CreateNew())
139+
{
140+
mapper.WhenMapping
141+
.To<Address>()
142+
.If((o, a) => a.Line2 == "Delete me")
143+
.MapToNull();
144+
145+
var source = new[]
146+
{
147+
new Address { Line1 = "Delete me" },
148+
new Address { Line2 = "Delete me" }
149+
};
150+
var result = mapper.Map(source).ToANew<ICollection<Address>>();
151+
152+
result.First().ShouldNotBeNull();
153+
result.First().Line1.ShouldBe("Delete me");
154+
155+
result.Second().ShouldNotBeNull();
156+
result.Second().Line2.ShouldBe("Delete me");
157+
}
158+
}
159+
160+
[Fact]
161+
public void ShouldMapCollectionElementNestedPropertiesToNull()
162+
{
163+
using (var mapper = Mapper.CreateNew())
164+
{
165+
mapper.WhenMapping
166+
.To<Address>()
167+
.If((o, a) => a.Line1 == null)
168+
.MapToNull();
169+
170+
var source = new[]
171+
{
172+
new CustomerViewModel { AddressLine1 = null },
173+
new CustomerViewModel { AddressLine1 = "Hello!" }
174+
};
175+
var result = mapper.Map(source).ToANew<IEnumerable<MysteryCustomer>>();
176+
177+
result.First().Address.ShouldBeNull();
178+
result.Second().Address.ShouldNotBeNull();
179+
result.Second().Address.Line1.ShouldBe("Hello!");
180+
}
181+
}
182+
183+
[Fact]
184+
public void ShouldErrorIfConditionsAreConfiguredForTheSameType()
185+
{
186+
var configEx = Should.Throw<MappingConfigurationException>(() =>
187+
{
188+
using (var mapper = Mapper.CreateNew())
189+
{
190+
mapper.WhenMapping
191+
.To<Address>()
192+
.If((o, a) => a.Line1 == null)
193+
.MapToNull();
194+
195+
mapper.WhenMapping
196+
.To<Address>()
197+
.If((o, a) => a.Line1 == string.Empty)
198+
.MapToNull();
199+
}
200+
});
201+
202+
configEx.Message.ShouldContain("already has");
203+
}
204+
}
205+
}

AgileMapper.UnitTests/packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
33
<package id="AgileObjects.NetStandardPolyfills" version="0.2.1" targetFramework="net461" />
4-
<package id="AgileObjects.ReadableExpressions" version="1.8.5" targetFramework="net461" />
4+
<package id="AgileObjects.ReadableExpressions" version="1.8.6" targetFramework="net461" />
55
<package id="Shouldly" version="2.8.2" targetFramework="net461" />
66
<package id="xunit" version="2.2.0" targetFramework="net461" />
77
<package id="xunit.abstractions" version="2.0.1" targetFramework="net461" />

AgileMapper/AgileMapper.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
<ItemGroup>
3232
<PackageReference Include="AgileObjects.NetStandardPolyfills" Version="0.2.1" />
33-
<PackageReference Include="AgileObjects.ReadableExpressions" Version="1.8.5" />
33+
<PackageReference Include="AgileObjects.ReadableExpressions" Version="1.8.6" />
3434
</ItemGroup>
3535

3636
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.0' ">

AgileMapper/Api/Configuration/IConditionalRootMappingConfigurator.cs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,25 @@ namespace AgileObjects.AgileMapper.Api.Configuration
88
public interface IConditionalRootMappingConfigurator<TSource, TTarget>
99
: IRootMappingConfigurator<TSource, TTarget>
1010
{
11-
/// <summary>
12-
/// Map the source type being configured to the derived target type specified by the type argument if
13-
/// the preceding condition evaluates to true.
14-
/// </summary>
15-
/// <typeparam name="TDerivedTarget">The derived target type to create.</typeparam>
16-
/// <returns>
17-
/// A MappingConfigContinuation to enable further configuration of mappings from and to the source and
18-
/// target type being configured.
11+
/// <summary>
12+
/// Map the source type being configured to the derived target type specified by the type argument if
13+
/// the preceding condition evaluates to true.
14+
/// </summary>
15+
/// <typeparam name="TDerivedTarget">The derived target type to create.</typeparam>
16+
/// <returns>
17+
/// A MappingConfigContinuation to enable further configuration of mappings from and to the source and
18+
/// target type being configured.
1919
/// </returns>
2020
MappingConfigContinuation<TSource, TTarget> MapTo<TDerivedTarget>()
2121
where TDerivedTarget : TTarget;
22+
23+
/// <summary>
24+
/// Map the target type being configured to null if the preceding condition evaluates to true.
25+
/// </summary>
26+
/// <returns>
27+
/// A MappingConfigContinuation to enable further configuration of mappings from and to the source and
28+
/// target type being configured.
29+
/// </returns>
30+
MappingConfigContinuation<TSource, TTarget> MapToNull();
2231
}
2332
}

AgileMapper/Api/Configuration/MappingConfigurator.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ public MappingConfigContinuation<TSource, TTarget> MapTo<TDerivedTarget>()
170170
return derivedTypePair.To<TDerivedTarget>();
171171
}
172172

173+
public MappingConfigContinuation<TSource, TTarget> MapToNull()
174+
{
175+
var condition = new MapToNullCondition(ConfigInfo.ForTargetType<TTarget>());
176+
177+
ConfigInfo.MapperContext.UserConfigurations.Add(condition);
178+
179+
return new MappingConfigContinuation<TSource, TTarget>(ConfigInfo);
180+
}
181+
173182
public DerivedPairTargetTypeSpecifier<TSource, TDerivedSource, TTarget> Map<TDerivedSource>() where TDerivedSource : TSource
174183
=> new DerivedPairTargetTypeSpecifier<TSource, TDerivedSource, TTarget>(ConfigInfo);
175184

0 commit comments

Comments
 (0)