Skip to content

Commit 0148d44

Browse files
authored
Auto reverse mapping (#105)
* Auto configuring the reverse of a configured source member data source Fixing enum pairing clone bug * Test coverage for configured constant and conditional configured source member reversal * Excluding conditional reverse data source configurations * Adding global option to switch on data source reversal * Adding global reverse configurations setting and config-scoped 'vice-versa' settings * Support for configuration-scoped data source reversal * Erroring on redundant reverse data source suppression * Erroring if redundant custom data source reversal configured * Erroring if a data source redundant from a previous reversal is configured * Renaming global method * Allowing replacement of implictly-created reverse data sources * Erroring on configuration of a data source which conflicts with an explicit reverse data source * Improving conflicting data source error messages * Renaming 'clonable' items to 'auto-created' * Support for mapping-scope data source reversal configuration * Extra test coverage * Extending test coverage * Erroring if non-member data source member-scoped data source reversal configured * Erroring if unmappable source members are auto-reverse data source'd * Refactoring member readability and access factory logic * Erroring if write-only target member is reversed * Test coverage for ignoring auto-reversed data source targets * Test coverage for invalid reversal scenarios * Lazy-loading lists where possible
1 parent 45bb654 commit 0148d44

File tree

46 files changed

+1536
-346
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1536
-346
lines changed

AgileMapper.UnitTests/AgileMapper.UnitTests.csproj

+2
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@
109109
<Compile Include="Configuration\WhenApplyingMapperConfigurations.cs" />
110110
<Compile Include="Configuration\WhenApplyingMapperConfigurationsIncorrectly.cs" />
111111
<Compile Include="Configuration\WhenConfiguringEntityMapping.cs" />
112+
<Compile Include="Configuration\WhenConfiguringReverseDataSources.cs" />
113+
<Compile Include="Configuration\WhenConfiguringReverseDataSourcesIncorrectly.cs" />
112114
<Compile Include="Configuration\WhenResolvingServices.cs" />
113115
<Compile Include="Configuration\WhenViewingMappingPlans.cs" />
114116
<Compile Include="Dictionaries\WhenMappingFromDictionariesOnToComplexTypes.cs" />

AgileMapper.UnitTests/Configuration/Inline/WhenConfiguringDataSourcesInlineIncorrectly.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public void ShouldErrorIfDuplicateDataSourceIsConfiguredInline()
5252
}
5353
});
5454

55-
inlineConfigEx.Message.ShouldContain("already has that configured data source");
55+
inlineConfigEx.Message.ShouldContain("already has configured data source 'ctx.Source.Value + \"!\"'");
5656
}
5757

5858
[Fact]
@@ -97,7 +97,7 @@ public void ShouldErrorIfRedundantDataSourceIsConfiguredInline()
9797
}
9898
});
9999

100-
inlineConfigEx.Message.ShouldContain("already has that configured data source");
100+
inlineConfigEx.Message.ShouldContain("already has configured data source 'Person.Id'");
101101
}
102102

103103
[Fact]
@@ -118,7 +118,7 @@ public void ShouldErrorIfConflictingDataSourceIsConfigured()
118118
}
119119
});
120120

121-
conflictEx.Message.ShouldContain("already has a configured data source");
121+
conflictEx.Message.ShouldContain("already has configured data source 'Customer.Id'");
122122
}
123123

124124
[Fact]

AgileMapper.UnitTests/Configuration/Inline/WhenViewingMappingPlans.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public void ShouldErrorIfConflictingDataSourcesConfiguredInline()
150150
.To(dto => dto.ProductId));
151151
});
152152

153-
configEx.Message.ShouldContain("already has a configured data source");
153+
configEx.Message.ShouldContain("already has configured data source 'p.ProductId + \" DTO\"'");
154154
}
155155
}
156156

AgileMapper.UnitTests/Configuration/WhenConfiguringDataSourcesIncorrectly.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public void ShouldErrorIfConflictingDataSourceIsConfigured()
152152
}
153153
});
154154

155-
conflictEx.Message.ShouldContain("already has a configured data source");
155+
conflictEx.Message.ShouldContain("already has configured data source 'Person.Id'");
156156
}
157157

158158
[Fact]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
namespace AgileObjects.AgileMapper.UnitTests.Configuration
2+
{
3+
using System;
4+
using Common;
5+
using TestClasses;
6+
#if !NET35
7+
using Xunit;
8+
#else
9+
using Fact = NUnit.Framework.TestAttribute;
10+
11+
[NUnit.Framework.TestFixture]
12+
#endif
13+
public class WhenConfiguringReverseDataSources
14+
{
15+
[Fact]
16+
public void ShouldReverseAConfiguredMemberByGlobalScope()
17+
{
18+
using (var mapper = Mapper.CreateNew())
19+
{
20+
mapper.WhenMapping
21+
.AutoReverseConfiguredDataSources()
22+
.AndWhenMapping
23+
.From<Person>()
24+
.To<PublicProperty<Guid>>()
25+
.Map(ctx => ctx.Source.Id)
26+
.To(pp => pp.Value);
27+
28+
var source = new Person { Id = Guid.NewGuid() };
29+
var result = mapper.Map(source).ToANew<PublicProperty<Guid>>();
30+
31+
result.Value.ShouldBe(source.Id);
32+
33+
var reverseResult = mapper.Map(result).ToANew<Person>();
34+
35+
reverseResult.Id.ShouldBe(source.Id);
36+
}
37+
}
38+
39+
[Fact]
40+
public void ShouldReverseAConfiguredMemberByMappingScope()
41+
{
42+
using (var mapper = Mapper.CreateNew())
43+
{
44+
mapper.WhenMapping
45+
.From<Person>()
46+
.To<PublicProperty<Guid>>()
47+
.AutoReverseConfiguredDataSources()
48+
.And
49+
.Map(ctx => ctx.Source.Id)
50+
.To(pp => pp.Value);
51+
52+
var source = new Person { Id = Guid.NewGuid() };
53+
var result = mapper.Map(source).ToANew<PublicProperty<Guid>>();
54+
55+
result.Value.ShouldBe(source.Id);
56+
57+
var reverseResult = mapper.Map(result).ToANew<Person>();
58+
59+
reverseResult.Id.ShouldBe(source.Id);
60+
}
61+
}
62+
63+
[Fact]
64+
public void ShouldReverseAConfiguredMemberByMemberScope()
65+
{
66+
using (var mapper = Mapper.CreateNew())
67+
{
68+
mapper.WhenMapping
69+
.From<Person>()
70+
.To<PublicProperty<Guid>>()
71+
.Map(ctx => ctx.Source.Id)
72+
.To(pp => pp.Value)
73+
.AndViceVersa();
74+
75+
var source = new Person { Id = Guid.NewGuid() };
76+
var result = mapper.Map(source).ToANew<PublicProperty<Guid>>();
77+
78+
result.Value.ShouldBe(source.Id);
79+
80+
var reverseResult = mapper.Map(result).ToANew<Person>();
81+
82+
reverseResult.Id.ShouldBe(source.Id);
83+
}
84+
}
85+
86+
[Fact]
87+
public void ShouldReverseAConfiguredMemberByOptInMemberScope()
88+
{
89+
using (var mapper = Mapper.CreateNew())
90+
{
91+
mapper.WhenMapping.AutoReverseConfiguredDataSources();
92+
93+
mapper.WhenMapping
94+
.From<Person>().To<PublicProperty<Guid>>()
95+
.DoNotAutoReverseConfiguredDataSources()
96+
.And
97+
.Map(p => p.Id, pp => pp.Value)
98+
.AndViceVersa();
99+
100+
var source = new Person { Id = Guid.NewGuid() };
101+
var result = mapper.Map(source).ToANew<PublicProperty<Guid>>();
102+
103+
result.Value.ShouldBe(source.Id);
104+
105+
var reverseResult = mapper.Map(result).ToANew<Person>();
106+
107+
reverseResult.Id.ShouldBe(source.Id);
108+
}
109+
}
110+
111+
[Fact]
112+
public void ShouldNotReverseAConfiguredMemberIfGlobalScopeOptedOutAtMappingScope()
113+
{
114+
using (var mapper = Mapper.CreateNew())
115+
{
116+
mapper.WhenMapping
117+
.AutoReverseConfiguredDataSources()
118+
.AndWhenMapping
119+
.From<Person>()
120+
.To<PublicProperty<Guid>>()
121+
.DoNotAutoReverseConfiguredDataSources()
122+
.And
123+
.Map(p => p.Id, pp => pp.Value);
124+
125+
var source = new Person { Id = Guid.NewGuid() };
126+
var result = mapper.Map(source).ToANew<PublicProperty<Guid>>();
127+
128+
result.Value.ShouldBe(source.Id);
129+
130+
var reverseResult = mapper.Map(result).ToANew<Person>();
131+
132+
reverseResult.Id.ShouldBeDefault();
133+
}
134+
}
135+
136+
[Fact]
137+
public void ShouldNotReverseAConfiguredMemberIfGlobalScopeOptedOutAtMemberScope()
138+
{
139+
using (var mapper = Mapper.CreateNew())
140+
{
141+
mapper.WhenMapping
142+
.AutoReverseConfiguredDataSources()
143+
.AndWhenMapping
144+
.From<Person>()
145+
.To<PublicProperty<Guid>>()
146+
.Map(ctx => ctx.Source.Id)
147+
.To(pp => pp.Value)
148+
.ButNotViceVersa();
149+
150+
var source = new Person { Id = Guid.NewGuid() };
151+
var result = mapper.Map(source).ToANew<PublicProperty<Guid>>();
152+
153+
result.Value.ShouldBe(source.Id);
154+
155+
var reverseResult = mapper.Map(result).ToANew<Person>();
156+
157+
reverseResult.Id.ShouldBeDefault();
158+
}
159+
}
160+
161+
[Fact]
162+
public void ShouldNotReverseAConfiguredMemberIfMappingScopeOptedOutAtMemberScope()
163+
{
164+
using (var mapper = Mapper.CreateNew())
165+
{
166+
mapper.WhenMapping
167+
.From<Person>()
168+
.To<PublicField<Guid>>()
169+
.AutoReverseConfiguredDataSources()
170+
.And
171+
.Map(p => p.Id, pf => pf.Value)
172+
.ButNotViceVersa();
173+
174+
var source = new Person { Id = Guid.NewGuid() };
175+
var result = mapper.Map(source).ToANew<PublicField<Guid>>();
176+
177+
result.Value.ShouldBe(source.Id);
178+
179+
var reverseResult = mapper.Map(result).ToANew<Person>();
180+
181+
reverseResult.Id.ShouldBeDefault();
182+
}
183+
}
184+
185+
[Fact]
186+
public void ShouldNotReverseAConfiguredConstant()
187+
{
188+
using (var mapper = Mapper.CreateNew())
189+
{
190+
const string GUID_VALUE = "21EFCF97-C7CF-42C7-B152-1C072E8C3BEA";
191+
192+
mapper.WhenMapping
193+
.AutoReverseConfiguredDataSources()
194+
.AndWhenMapping
195+
.From<Person>()
196+
.To<PublicField<Guid>>()
197+
.Map(GUID_VALUE)
198+
.To(pf => pf.Value);
199+
200+
var source = new Person { Id = Guid.NewGuid() };
201+
var result = mapper.Map(source).ToANew<PublicField<Guid>>();
202+
203+
result.Value.ShouldBe(new Guid(GUID_VALUE));
204+
205+
var reverseResult = mapper.Map(result).ToANew<Person>();
206+
207+
reverseResult.Id.ShouldBeDefault();
208+
}
209+
}
210+
211+
[Fact]
212+
public void ShouldNotReverseAConditionalConfiguredMember()
213+
{
214+
using (var mapper = Mapper.CreateNew())
215+
{
216+
mapper.WhenMapping
217+
.AutoReverseConfiguredDataSources()
218+
.AndWhenMapping
219+
.From<ProductDto>()
220+
.To<PublicProperty<int>>()
221+
.If(ctx => ctx.Source.Price > 100)
222+
.Map(dto => dto.ProductId, pp => pp.Value);
223+
224+
var lowPriceSource = new ProductDto { ProductId = "123", Price = 99 };
225+
var lowPriceResult = mapper.Map(lowPriceSource).ToANew<PublicProperty<int>>();
226+
227+
lowPriceResult.Value.ShouldBeDefault();
228+
229+
var highPriceSource = new ProductDto { ProductId = "456", Price = 101 };
230+
var highPriceResult = mapper.Map(highPriceSource).ToANew<PublicProperty<int>>();
231+
232+
highPriceResult.Value.ShouldBe(456);
233+
234+
var lowPriceReverseResult = mapper.Map(lowPriceResult).ToANew<ProductDto>();
235+
236+
lowPriceReverseResult.ProductId.ShouldBeNull();
237+
238+
var highPriceReverseResult = mapper.Map(highPriceResult).ToANew<ProductDto>();
239+
240+
highPriceReverseResult.ProductId.ShouldBeNull();
241+
}
242+
}
243+
244+
[Fact]
245+
public void ShouldAllowReplacementOfAutoReversedConfiguredMember()
246+
{
247+
using (var mapper = Mapper.CreateNew())
248+
{
249+
mapper.WhenMapping.AutoReverseConfiguredDataSources();
250+
251+
mapper.WhenMapping
252+
.From<Person>()
253+
.To<PublicTwoFields<Guid, Guid>>()
254+
.Map(p => p.Id, ptf => ptf.Value1);
255+
256+
mapper.WhenMapping
257+
.From<PublicTwoFields<Guid, Guid>>()
258+
.To<Person>()
259+
.Map(ptf => ptf.Value2, p => p.Id);
260+
261+
var source = new Person { Id = Guid.NewGuid() };
262+
var result = mapper.Map(source).ToANew<PublicTwoFields<Guid, Guid>>();
263+
264+
result.Value1.ShouldBe(source.Id);
265+
result.Value2.ShouldBe(source.Id); // ...from reverse of Value2 -> Id
266+
267+
result.Value2 = Guid.NewGuid();
268+
269+
var reverseResult = mapper.Map(result).ToANew<Person>();
270+
271+
reverseResult.Id.ShouldBe(result.Value2);
272+
}
273+
}
274+
275+
[Fact]
276+
public void ShouldAllowIgnoreOfAutoReversedConfiguredMember()
277+
{
278+
using (var mapper = Mapper.CreateNew())
279+
{
280+
mapper.WhenMapping
281+
.From<Person>()
282+
.To<PublicTwoFields<Guid, Guid>>()
283+
.AutoReverseConfiguredDataSources()
284+
.And
285+
.Map(p => p.Id, ptf => ptf.Value1);
286+
287+
mapper.WhenMapping
288+
.From<PublicTwoFields<Guid, Guid>>()
289+
.To<Person>()
290+
.Ignore(p => p.Id);
291+
292+
var source = new Person { Id = Guid.NewGuid() };
293+
var result = mapper.Map(source).ToANew<PublicTwoFields<Guid, Guid>>();
294+
295+
result.Value1.ShouldBe(source.Id);
296+
result.Value2.ShouldBeDefault();
297+
298+
var reverseResult = mapper.Map(result).ToANew<Person>();
299+
300+
reverseResult.Id.ShouldBeDefault();
301+
}
302+
}
303+
}
304+
}

0 commit comments

Comments
 (0)