Skip to content

Commit 3bbab67

Browse files
committed
Adding support for passing type activators into configuration;
Adding `Type` entries to `Map`;
1 parent bf8e28e commit 3bbab67

File tree

5 files changed

+313
-35
lines changed

5 files changed

+313
-35
lines changed

Slapper.AutoMapper.Tests/Slapper.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<Compile Include="SimpleTypeConversionTests.cs" />
6767
<Compile Include="TestBase.cs" />
6868
<Compile Include="TestHelpers.cs" />
69+
<Compile Include="TypeActivatorTests.cs" />
6970
</ItemGroup>
7071
<ItemGroup>
7172
<ProjectReference Include="..\Slapper.AutoMapper\Slapper.csproj">
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using NUnit.Framework;
4+
5+
namespace Slapper.Tests
6+
{
7+
public class TypeActivatorTests
8+
{
9+
public interface IClass
10+
{
11+
string Id { get; set; }
12+
13+
INestedClass Nested { get; set; }
14+
}
15+
16+
public class Class : IClass
17+
{
18+
public string Id { get; set; }
19+
20+
public INestedClass Nested { get; set; }
21+
}
22+
23+
public interface INestedClass
24+
{
25+
int Count { get; set; }
26+
27+
Enum Enum { get; set; }
28+
}
29+
30+
public class NestedClass : INestedClass
31+
{
32+
public int Count { get; set; }
33+
34+
public Enum Enum { get; set; }
35+
}
36+
37+
public enum Enum
38+
{
39+
One,
40+
Two
41+
}
42+
43+
public class TypeActivator : AutoMapper.Configuration.ITypeActivator
44+
{
45+
private readonly IDictionary<Type, Type> supportedTypes = new Dictionary<Type, Type>
46+
{
47+
{ typeof(IClass), typeof(Class) },
48+
{ typeof(INestedClass), typeof(NestedClass) }
49+
};
50+
51+
public object Create(Type type)
52+
{
53+
var concreteType = this.supportedTypes[type];
54+
return Activator.CreateInstance(concreteType);
55+
}
56+
57+
public bool CanCreate(Type type)
58+
{
59+
return this.supportedTypes.ContainsKey(type);
60+
}
61+
62+
public int Order { get; } = 1;
63+
}
64+
65+
[Test]
66+
public void Can_Use_Registered_TypeActivators_WithInterfaces()
67+
{
68+
// Arrange
69+
const string id = "1";
70+
const int count = 2;
71+
const Enum enu = Enum.Two;
72+
73+
var dictionary = new Dictionary<string, object>
74+
{
75+
{ "Id", id },
76+
{ "Nested_Count", count },
77+
{ "Nested_Enum", enu }
78+
};
79+
80+
AutoMapper.Configuration.TypeActivators.Add(new TypeActivator());
81+
82+
// Act
83+
var result = Slapper.AutoMapper.Map<IClass>(dictionary);
84+
85+
// Assert
86+
Assert.NotNull(result);
87+
Assert.That(result.Id == id);
88+
Assert.That(result.Nested.Enum == enu);
89+
Assert.That(result.Nested.Count == count);
90+
}
91+
92+
public interface IClass2
93+
{
94+
string Id { get; set; }
95+
}
96+
97+
public class Class2 : IClass2
98+
{
99+
public string Id { get; set; }
100+
}
101+
102+
public class Class2Copy : IClass2
103+
{
104+
public string Id { get; set; }
105+
}
106+
107+
public class Class2TypeActivator : AutoMapper.Configuration.ITypeActivator
108+
{
109+
public object Create(Type type)
110+
{
111+
return new Class2();
112+
}
113+
114+
public bool CanCreate(Type type)
115+
{
116+
return type.IsAssignableFrom(typeof(IClass2));
117+
}
118+
119+
public int Order { get; } = 2;
120+
}
121+
122+
public class Class2CopyTypeActivator : AutoMapper.Configuration.ITypeActivator
123+
{
124+
public object Create(Type type)
125+
{
126+
return new Class2Copy();
127+
}
128+
129+
public bool CanCreate(Type type)
130+
{
131+
return type.IsAssignableFrom(typeof(IClass2));
132+
}
133+
134+
public int Order { get; } = 1;
135+
}
136+
137+
[Test]
138+
public void TypeActivator_Order_Is_Adhered_To()
139+
{
140+
// Arrange
141+
const string id = "1";
142+
const int count = 2;
143+
const Enum enu = Enum.Two;
144+
145+
var dictionary = new Dictionary<string, object>
146+
{
147+
{ "Id", id },
148+
{ "Nested_Count", count },
149+
{ "Nested_Enum", enu }
150+
};
151+
152+
AutoMapper.Configuration.TypeActivators.Add(new Class2TypeActivator());
153+
AutoMapper.Configuration.TypeActivators.Add(new Class2CopyTypeActivator());
154+
155+
// Act
156+
var result = Slapper.AutoMapper.Map<IClass2>(dictionary);
157+
158+
// Assert
159+
Assert.NotNull(result);
160+
Assert.That(result.Id == id);
161+
Assert.That(result.GetType() == typeof(Class2Copy));
162+
}
163+
}
164+
}

Slapper.AutoMapper/Slapper.AutoMapper.Configuration.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ static Configuration()
7878
/// </summary>
7979
public static readonly List<ITypeConverter> TypeConverters = new List<ITypeConverter>();
8080

81+
/// <summary>
82+
/// Activators to instantiate types.
83+
/// </summary>
84+
public static readonly List<ITypeActivator> TypeActivators = new List<ITypeActivator>();
85+
8186
/// <summary>
8287
/// Applies default conventions for finding identifiers.
8388
/// </summary>
@@ -281,6 +286,31 @@ public bool CanConvert(object value, Type type)
281286

282287
#endregion
283288
}
289+
290+
/// <summary>
291+
/// Defines an interface for an activator for a specific type.
292+
/// </summary>
293+
public interface ITypeActivator
294+
{
295+
/// <summary>
296+
/// Creates the type.
297+
/// </summary>
298+
/// <param name="type">The type to create.</param>
299+
/// <returns>The created type.</returns>
300+
object Create(Type type);
301+
302+
/// <summary>
303+
/// Indicates whether it can create the type.
304+
/// </summary>
305+
/// <param name="type">The type to create.</param>
306+
/// <returns>Boolean response.</returns>
307+
bool CanCreate(Type type);
308+
309+
/// <summary>
310+
/// The order to try the activator in.
311+
/// </summary>
312+
int Order { get; }
313+
}
284314
}
285315

286316
#endregion Configuration

Slapper.AutoMapper/Slapper.AutoMapper.InternalHelpers.cs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,23 @@ public static Dictionary<string, object> GetFieldsAndProperties(Type type)
8888
/// </returns>
8989
public static object CreateInstance(Type type)
9090
{
91-
return type == typeof(string) ? string.Empty : Activator.CreateInstance(type);
91+
if (type == typeof(string))
92+
{
93+
return string.Empty;
94+
}
95+
96+
if (Configuration.TypeActivators.Count > 0)
97+
{
98+
foreach (var typeActivator in Configuration.TypeActivators.OrderBy(ta => ta.Order))
99+
{
100+
if (typeActivator.CanCreate(type))
101+
{
102+
return typeActivator.Create(type);
103+
}
104+
}
105+
}
106+
107+
return Activator.CreateInstance(type);
92108
}
93109

94110
/// <summary>
@@ -466,23 +482,26 @@ internal static object Map(IDictionary<string, object> dictionary, object instan
466482
// hasn't been initialized, then this will return null.
467483
object nestedInstance = GetMemberValue(member, instance);
468484

469-
// If the member is null and is a class, try to create an instance of the type
470-
if (nestedInstance == null && memberType.IsClass)
485+
var genericCollectionType = typeof(IEnumerable<>);
486+
var isEnumerableType = memberType.IsGenericType && genericCollectionType.IsAssignableFrom(memberType.GetGenericTypeDefinition())
487+
|| memberType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericCollectionType);
488+
489+
// If the member is null and is a class or interface (not ienumerable), try to create an instance of the type
490+
if (nestedInstance == null && (memberType.IsClass || (memberType.IsInterface && !isEnumerableType)))
471491
{
472492
if (memberType.IsArray)
473493
{
474494
nestedInstance = new ArrayList().ToArray(memberType.GetElementType());
475495
}
476496
else
477497
{
478-
nestedInstance = typeof(IEnumerable).IsAssignableFrom(memberType) ? CreateInstance(memberType) : GetInstance(memberType, newDictionary, parentInstance == null ? 0 : parentInstance.GetHashCode()).Item2;
498+
nestedInstance = typeof(IEnumerable).IsAssignableFrom(memberType)
499+
? CreateInstance(memberType)
500+
: GetInstance(memberType, newDictionary, parentInstance == null ? 0 : parentInstance.GetHashCode()).Item2;
479501
}
480502
}
481503

482-
Type genericCollectionType = typeof(IEnumerable<>);
483-
484-
if (memberType.IsGenericType && genericCollectionType.IsAssignableFrom(memberType.GetGenericTypeDefinition())
485-
|| memberType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericCollectionType))
504+
if (isEnumerableType)
486505
{
487506
var innerType = memberType.GetGenericArguments().FirstOrDefault() ?? memberType.GetElementType();
488507
nestedInstance = MapCollection(innerType, newDictionary, nestedInstance, instance);

0 commit comments

Comments
 (0)