Skip to content

Commit a85a9bf

Browse files
authored
Configurable entity mapping (#103)
* Support for globally enabling entity key mapping * Support for configuring target type-specific entity key mapping * Ordering entity key mapping settings * Erroring on invalid entity key mapping configuration * Support for inline entity mapping configuration
1 parent 9a6d3e1 commit a85a9bf

File tree

10 files changed

+467
-40
lines changed

10 files changed

+467
-40
lines changed

AgileMapper.UnitTests/AgileMapper.UnitTests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
<Compile Include="Configuration\Inline\WhenConfiguringDataSourcesInline.cs" />
9595
<Compile Include="Configuration\Inline\WhenConfiguringDataSourcesInlineIncorrectly.cs" />
9696
<Compile Include="Configuration\Inline\WhenConfiguringDerivedTypesInline.cs" />
97+
<Compile Include="Configuration\Inline\WhenConfiguringEntityMappingInline.cs" />
9798
<Compile Include="Configuration\Inline\WhenConfiguringEnumMappingInline.cs" />
9899
<Compile Include="Configuration\Inline\WhenConfiguringNameMatchingInline.cs" />
99100
<Compile Include="Configuration\Inline\WhenConfiguringObjectCreationInline.cs" />
@@ -107,6 +108,7 @@
107108
<Compile Include="Configuration\Inline\WhenViewingMappingPlans.cs" />
108109
<Compile Include="Configuration\WhenApplyingMapperConfigurations.cs" />
109110
<Compile Include="Configuration\WhenApplyingMapperConfigurationsIncorrectly.cs" />
111+
<Compile Include="Configuration\WhenConfiguringEntityMapping.cs" />
110112
<Compile Include="Configuration\WhenResolvingServices.cs" />
111113
<Compile Include="Configuration\WhenViewingMappingPlans.cs" />
112114
<Compile Include="Dictionaries\WhenMappingFromDictionariesOnToComplexTypes.cs" />
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
namespace AgileObjects.AgileMapper.UnitTests.Configuration.Inline
2+
{
3+
using AgileMapper.Configuration;
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 WhenConfiguringEntityMappingInline
14+
{
15+
[Fact]
16+
public void ShouldMapEntityKeys()
17+
{
18+
using (var mapper = Mapper.CreateNew())
19+
{
20+
var source = new { Id = 999, Price = 99.99 };
21+
var result = mapper.Map(source).ToANew<ProductEntity>(c => c.MapEntityKeys());
22+
23+
result.Id.ShouldBe(999);
24+
result.Price.ShouldBe(99.99);
25+
}
26+
}
27+
28+
[Fact]
29+
public void ShouldIgnoreEntityKeysForASpecificMapping()
30+
{
31+
using (var mapper = Mapper.CreateNew())
32+
{
33+
mapper.WhenMapping.MapEntityKeys();
34+
35+
var source = new { Id = 987, Name = "Barney" };
36+
37+
var defaultResult = mapper.Map(source).ToANew<CategoryEntity>();
38+
39+
defaultResult.Id.ShouldBe(987);
40+
defaultResult.Name.ShouldBe("Barney");
41+
42+
var ignoreKeysResult = mapper.Map(source).ToANew<CategoryEntity>(c => c.IgnoreEntityKeys());
43+
44+
ignoreKeysResult.Id.ShouldBeDefault();
45+
ignoreKeysResult.Name.ShouldBe("Barney");
46+
}
47+
}
48+
49+
[Fact]
50+
public void ShouldErrorIfRedundantMapKeysConfigured()
51+
{
52+
var configEx = Should.Throw<MappingConfigurationException>(() =>
53+
{
54+
using (var mapper = Mapper.CreateNew())
55+
{
56+
mapper.WhenMapping.MapEntityKeys();
57+
mapper.Map(new ProductEntity()).ToANew<ProductEntity>(c => c.MapEntityKeys());
58+
}
59+
});
60+
61+
configEx.Message.ShouldContain("already enabled");
62+
}
63+
64+
[Fact]
65+
public void ShouldErrorIfRedundantIgnoreKeysConfigured()
66+
{
67+
var configEx = Should.Throw<MappingConfigurationException>(() =>
68+
{
69+
using (var mapper = Mapper.CreateNew())
70+
{
71+
mapper.Map(new ProductEntity()).ToANew<ProductEntity>(c => c.IgnoreEntityKeys());
72+
}
73+
});
74+
75+
configEx.Message.ShouldContain("disabled by default");
76+
}
77+
}
78+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
namespace AgileObjects.AgileMapper.UnitTests.Configuration
2+
{
3+
using AgileMapper.Configuration;
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 WhenConfiguringEntityMapping
14+
{
15+
[Fact]
16+
public void ShouldMapEntityKeys()
17+
{
18+
using (var mapper = Mapper.CreateNew())
19+
{
20+
mapper.WhenMapping.MapEntityKeys();
21+
22+
var source = new { Id = 456 };
23+
var result = mapper.Map(source).ToANew<ProductEntity>();
24+
25+
result.Id.ShouldBe(456);
26+
}
27+
}
28+
29+
[Fact]
30+
public void ShouldMapEntityKeysForASpecificTargetType()
31+
{
32+
using (var mapper = Mapper.CreateNew())
33+
{
34+
mapper.WhenMapping
35+
.To<ProductEntity>()
36+
.MapEntityKeys();
37+
38+
var source = new { Id = 123 };
39+
var matchingResult = mapper.Map(source).ToANew<ProductEntity>();
40+
41+
matchingResult.Id.ShouldBe(123);
42+
43+
var nonMatchingTargetResult = mapper.Map(source).ToANew<OrderEntity>();
44+
45+
nonMatchingTargetResult.Id.ShouldBeDefault();
46+
}
47+
}
48+
49+
[Fact]
50+
public void ShouldIgnoreEntityKeysForASpecificSourceAndTargetType()
51+
{
52+
using (var mapper = Mapper.CreateNew())
53+
{
54+
var source = new { Id = 999 };
55+
56+
mapper.WhenMapping
57+
.MapEntityKeys()
58+
.AndWhenMapping
59+
.From(source).To<OrderEntity>()
60+
.IgnoreEntityKeys();
61+
62+
var matchingResult = mapper.Map(source).ToANew<OrderEntity>();
63+
64+
matchingResult.Id.ShouldBeDefault();
65+
66+
var nonMatchingSourceResult = mapper.Map(new { Id = 987, Name = "Fred" }).ToANew<CategoryEntity>();
67+
68+
nonMatchingSourceResult.Id.ShouldBe(987);
69+
70+
var nonMatchingTargetResult = mapper.Map(source).ToANew<CategoryEntity>();
71+
72+
nonMatchingTargetResult.Id.ShouldBe(999);
73+
}
74+
}
75+
76+
[Fact]
77+
public void ShouldErrorIfDuplicateMapKeysConfigured()
78+
{
79+
var configEx = Should.Throw<MappingConfigurationException>(() =>
80+
{
81+
using (var mapper = Mapper.CreateNew())
82+
{
83+
mapper.WhenMapping
84+
.From<ProductDto>().To<ProductEntity>()
85+
.MapEntityKeys()
86+
.And
87+
.MapEntityKeys();
88+
}
89+
});
90+
91+
configEx.Message.ShouldContain("already enabled");
92+
}
93+
94+
[Fact]
95+
public void ShouldErrorIfDuplicateIgnoreKeysConfigured()
96+
{
97+
var configEx = Should.Throw<MappingConfigurationException>(() =>
98+
{
99+
using (var mapper = Mapper.CreateNew())
100+
{
101+
mapper.WhenMapping
102+
.MapEntityKeys()
103+
.AndWhenMapping
104+
.From<OrderDto>().To<OrderEntity>()
105+
.IgnoreEntityKeys()
106+
.And
107+
.IgnoreEntityKeys();
108+
}
109+
});
110+
111+
configEx.Message.ShouldContain("already disabled");
112+
}
113+
114+
[Fact]
115+
public void ShouldErrorIfRedundantMapKeysConfigured()
116+
{
117+
var configEx = Should.Throw<MappingConfigurationException>(() =>
118+
{
119+
using (var mapper = Mapper.CreateNew())
120+
{
121+
mapper.WhenMapping
122+
.MapEntityKeys()
123+
.AndWhenMapping
124+
.From<ProductEntity>().To<ProductEntity>()
125+
.MapEntityKeys();
126+
}
127+
});
128+
129+
configEx.Message.ShouldContain("already enabled");
130+
}
131+
132+
[Fact]
133+
public void ShouldErrorIfRedundantIgnoreKeysConfigured()
134+
{
135+
var configEx = Should.Throw<MappingConfigurationException>(() =>
136+
{
137+
using (var mapper = Mapper.CreateNew())
138+
{
139+
mapper.WhenMapping.To<ProductEntity>().IgnoreEntityKeys();
140+
}
141+
});
142+
143+
configEx.Message.ShouldContain("disabled by default");
144+
}
145+
146+
[Fact]
147+
public void ShouldErrorIfConflictingMapKeysConfigured()
148+
{
149+
var configEx = Should.Throw<MappingConfigurationException>(() =>
150+
{
151+
using (var mapper = Mapper.CreateNew())
152+
{
153+
mapper.WhenMapping
154+
.MapEntityKeys()
155+
.AndWhenMapping
156+
.To<CategoryEntity>()
157+
.IgnoreEntityKeys()
158+
.And
159+
.MapEntityKeys();
160+
}
161+
});
162+
163+
configEx.Message.ShouldContain("already been disabled");
164+
}
165+
166+
[Fact]
167+
public void ShouldErrorIfConflictingIgnoreKeysConfigured()
168+
{
169+
var configEx = Should.Throw<MappingConfigurationException>(() =>
170+
{
171+
using (var mapper = Mapper.CreateNew())
172+
{
173+
mapper.WhenMapping
174+
.To<ProductEntity>()
175+
.MapEntityKeys()
176+
.And
177+
.IgnoreEntityKeys();
178+
}
179+
});
180+
181+
configEx.Message.ShouldContain("already been enabled");
182+
}
183+
}
184+
}

AgileMapper/Api/Configuration/IFullMappingSettings.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,26 @@ public interface IFullMappingSettings<TSource, TTarget> : IConditionalMappingCon
7070
/// </returns>
7171
IFullMappingSettings<TSource, TTarget> MapNullCollectionsToNull();
7272

73+
/// <summary>
74+
/// Map entity key values for the source and target types being configured.
75+
/// </summary>
76+
/// <returns>
77+
/// An IFullMappingSettings{TSource, TTarget} with which to configure further settings for the source and
78+
/// target types being configured.
79+
/// </returns>
80+
IFullMappingSettings<TSource, TTarget> MapEntityKeys();
81+
82+
/// <summary>
83+
/// Ignore entity key values for the source and target types being configured. Use this method
84+
/// to disable entity key mapping for specific Types when global entity key mapping has been
85+
/// enabled with Mapper.WhenMapping.MapEntityKeys().
86+
/// </summary>
87+
/// <returns>
88+
/// An IFullMappingSettings{TSource, TTarget} with which to configure further settings for the
89+
/// source and target types being configured.
90+
/// </returns>
91+
IFullMappingSettings<TSource, TTarget> IgnoreEntityKeys();
92+
7393
/// <summary>
7494
/// Configure this mapper to pair the given <paramref name="enumMember"/> with a member of another
7595
/// enum Type.

AgileMapper/Api/Configuration/MappingConfigStartingPoint.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public IGlobalMappingSettings UseNamePatterns(params string[] patterns)
231231
/// </returns>
232232
public IGlobalMappingSettings MaintainIdentityIntegrity()
233233
{
234-
MapperContext.UserConfigurations.Add(MappedObjectCachingSettings.CacheAll);
234+
MapperContext.UserConfigurations.Add(MappedObjectCachingSetting.CacheAll);
235235
return this;
236236
}
237237

@@ -247,7 +247,7 @@ public IGlobalMappingSettings MaintainIdentityIntegrity()
247247
/// </returns>
248248
public IGlobalMappingSettings DisableObjectTracking()
249249
{
250-
MapperContext.UserConfigurations.Add(MappedObjectCachingSettings.CacheNone);
250+
MapperContext.UserConfigurations.Add(MappedObjectCachingSetting.CacheNone);
251251
return this;
252252
}
253253

@@ -263,6 +263,18 @@ public IGlobalMappingSettings MapNullCollectionsToNull()
263263
return this;
264264
}
265265

266+
/// <summary>
267+
/// Map entity key values for all source and target types.
268+
/// </summary>
269+
/// <returns>
270+
/// This <see cref="IGlobalMappingSettings"/> with which to globally configure other mapping aspects.
271+
/// </returns>
272+
public IGlobalMappingSettings MapEntityKeys()
273+
{
274+
MapperContext.UserConfigurations.Add(EntityKeyMappingSetting.MapAllKeys);
275+
return this;
276+
}
277+
266278
/// <summary>
267279
/// Throw an exception upon creation of a mapper if the mapping plan has any target members which will not be mapped,
268280
/// maps from a source enum to a target enum which does not support all of its values, or includes complex types which

AgileMapper/Api/Configuration/MappingConfigurator.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,7 @@ private FactorySpecifier<TSource, TTarget, TObject> CreateFactorySpecifier<TObje
203203

204204
public IFullMappingSettings<TSource, TTarget> PassExceptionsTo(Action<IMappingExceptionData<TSource, TTarget>> callback)
205205
{
206-
var exceptionCallback = new ExceptionCallback(ConfigInfo, callback.ToConstantExpression());
207-
208-
MapperContext.UserConfigurations.Add(exceptionCallback);
206+
MapperContext.UserConfigurations.Add(new ExceptionCallback(ConfigInfo, callback.ToConstantExpression()));
209207
return this;
210208
}
211209

@@ -215,17 +213,23 @@ public IFullMappingSettings<TSource, TTarget> PassExceptionsTo(Action<IMappingEx
215213

216214
private IFullMappingSettings<TSource, TTarget> SetMappedObjectCaching(bool cache)
217215
{
218-
var settings = new MappedObjectCachingSettings(ConfigInfo, cache);
219-
220-
MapperContext.UserConfigurations.Add(settings);
216+
MapperContext.UserConfigurations.Add(new MappedObjectCachingSetting(ConfigInfo, cache));
221217
return this;
222218
}
223219

224220
public IFullMappingSettings<TSource, TTarget> MapNullCollectionsToNull()
225221
{
226-
var nullSetting = new NullCollectionsSetting(ConfigInfo);
222+
MapperContext.UserConfigurations.Add(new NullCollectionsSetting(ConfigInfo));
223+
return this;
224+
}
227225

228-
MapperContext.UserConfigurations.Add(nullSetting);
226+
public IFullMappingSettings<TSource, TTarget> MapEntityKeys() => SetEntityKeyMapping(mapKeys: true);
227+
228+
public IFullMappingSettings<TSource, TTarget> IgnoreEntityKeys() => SetEntityKeyMapping(mapKeys: false);
229+
230+
private IFullMappingSettings<TSource, TTarget> SetEntityKeyMapping(bool mapKeys)
231+
{
232+
MapperContext.UserConfigurations.Add(new EntityKeyMappingSetting(ConfigInfo, mapKeys));
229233
return this;
230234
}
231235

0 commit comments

Comments
 (0)