Skip to content

Commit 846adf8

Browse files
committed
Increasing MapperConfiguration test coverage / Creating single service dictionary for all MapperConfigurations
1 parent f24845f commit 846adf8

File tree

4 files changed

+85
-25
lines changed

4 files changed

+85
-25
lines changed

AgileMapper.UnitTests.MoreTestClasses/ServiceDictionaryMapperConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class ServiceDictionaryMapperConfiguration : MapperConfiguration
77
{
88
protected override void Configure()
99
{
10-
var mappersByName = GetServiceOrNull<Dictionary<string, IMapper>>();
10+
var mappersByName = GetServiceOrThrow<Dictionary<string, IMapper>>();
1111

1212
var mapper1 = CreateNewMapper();
1313

AgileMapper.UnitTests/Configuration/WhenApplyingMapperConfigurations.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,27 @@ public void ShouldFilterMapperConfigurationsFromGivenAssemblies()
107107
});
108108
}
109109

110+
[Fact]
111+
public void ShouldApplyMapperConfigurationCallbacks()
112+
{
113+
using (var mapper = Mapper.CreateNew())
114+
{
115+
var data = new Dictionary<string, object>();
116+
117+
mapper.WhenMapping
118+
.UseConfigurations.From<CallbacksMapperConfiguration>(data);
119+
120+
var source = new Address { Line1 = "One", Line2 = "Two" };
121+
var result = mapper.DeepClone(source);
122+
123+
result.Line1.ShouldBe("One");
124+
result.Line2.ShouldBe("Two");
125+
126+
data["SourceAddress"].ShouldBe(source);
127+
data["TargetAddress"].ShouldBe(result);
128+
}
129+
}
130+
110131
#region Helper Classes
111132

112133
public class PfiToPfsMapperConfiguration : MapperConfiguration
@@ -154,6 +175,28 @@ public static void VerifyConfigured(IMapper mapper)
154175
}
155176
}
156177

178+
public class CallbacksMapperConfiguration : MapperConfiguration
179+
{
180+
protected override void Configure()
181+
{
182+
var dataByKey = GetServiceOrNull<Dictionary<string, object>>();
183+
184+
if (dataByKey == null)
185+
{
186+
return;
187+
}
188+
189+
WhenMapping
190+
.From<Address>()
191+
.To<Address>()
192+
.Before.MappingBegins
193+
.Call(ctx => dataByKey["SourceAddress"] = ctx.Source)
194+
.And
195+
.After.MappingEnds
196+
.Call(ctx => dataByKey["TargetAddress"] = ctx.Target);
197+
}
198+
}
199+
157200
#endregion
158201
}
159202
}

AgileMapper/Api/Configuration/MapperConfigurationSpecifier.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
/// </summary>
1515
public class MapperConfigurationSpecifier
1616
{
17+
private static readonly IDictionary<Type, object> _noServices = new Dictionary<Type, object>();
18+
1719
private readonly IMapper _mapper;
1820

1921
internal MapperConfigurationSpecifier(IMapper mapper)
@@ -39,7 +41,7 @@ public MapperConfigurationSpecifier From<TConfiguration>(params object[] service
3941
{
4042
var configuration = new TConfiguration();
4143

42-
Apply(configuration, services);
44+
Apply(configuration, CreateServiceCache(services));
4345
return this;
4446
}
4547

@@ -127,17 +129,36 @@ private void ApplyConfigurationsIn(Assembly assembly, ICollection<object> servic
127129
.Filter(t => !t.IsAbstract() && t.IsDerivedFrom(typeof(MapperConfiguration)))
128130
.Project(t => (MapperConfiguration)Activator.CreateInstance(t));
129131

132+
var servicesByType = CreateServiceCache(services);
133+
130134
foreach (var configuration in configurations)
131135
{
132-
Apply(configuration, services);
136+
Apply(configuration, servicesByType);
137+
}
138+
}
139+
140+
private static IDictionary<Type, object> CreateServiceCache(ICollection<object> services)
141+
{
142+
if (services.None())
143+
{
144+
return _noServices;
145+
}
146+
147+
var serviceCache = new Dictionary<Type, object>(services.Count);
148+
149+
foreach (var service in services)
150+
{
151+
serviceCache[service.GetType()] = service;
133152
}
153+
154+
return serviceCache;
134155
}
135156

136-
private void Apply(MapperConfiguration configuration, ICollection<object> services)
157+
private void Apply(MapperConfiguration configuration, IDictionary<Type, object> servicesByType)
137158
{
138159
try
139160
{
140-
configuration.ApplyTo(_mapper, services);
161+
configuration.ApplyTo(_mapper, servicesByType);
141162
}
142163
catch (Exception ex)
143164
{

AgileMapper/Configuration/MapperConfiguration.cs

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,42 @@
55
using System.Linq;
66
using Api;
77
using Api.Configuration;
8-
using Extensions.Internal;
98
using Queryables.Api;
9+
using ReadableExpressions.Extensions;
1010

1111
/// <summary>
1212
/// Base class for multiple, dedicated mapper configuration classes.
1313
/// </summary>
1414
public abstract class MapperConfiguration
1515
{
16-
private Dictionary<Type, object> _servicesByType;
16+
private IDictionary<Type, object> _servicesByType;
1717
private IMapper _mapper;
1818

19-
internal void ApplyTo(IMapper mapper, ICollection<object> services)
19+
internal void ApplyTo(IMapper mapper, IDictionary<Type, object> servicesByType)
2020
{
2121
_mapper = mapper;
22-
23-
if (services.Any())
24-
{
25-
_servicesByType = CreateServiceCache(services);
26-
}
22+
_servicesByType = servicesByType;
2723

2824
Configure();
2925
}
3026

31-
private static Dictionary<Type, object> CreateServiceCache(ICollection<object> services)
32-
{
33-
var serviceCache = new Dictionary<Type, object>(services.Count);
34-
35-
foreach (var service in services)
36-
{
37-
serviceCache[service.GetType()] = service;
38-
}
39-
40-
return serviceCache;
41-
}
42-
4327
/// <summary>
4428
/// Configure how mappings should be performed.
4529
/// </summary>
4630
protected abstract void Configure();
4731

32+
/// <summary>
33+
/// Get the registered <typeparamref name="TService"/> instance to assist with mapper configuration.
34+
/// </summary>
35+
/// <typeparam name="TService">The type of the service to retrieve.</typeparam>
36+
/// <returns>The registered <typeparamref name="TService"/> instance, or null if none is registered.</returns>
37+
protected TService GetServiceOrThrow<TService>()
38+
where TService : class
39+
{
40+
return GetServiceOrNull<TService>()
41+
?? throw new MappingConfigurationException($"No service of type {typeof(TService).GetFriendlyName()} is available");
42+
}
43+
4844
/// <summary>
4945
/// Get the registered <typeparamref name="TService"/> instance to assist with mapper configuration.
5046
/// </summary>

0 commit comments

Comments
 (0)