Skip to content

Commit c8544c8

Browse files
committed
Merge branch '#38'
2 parents 59e1401 + 3bbab67 commit c8544c8

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
@@ -67,6 +67,7 @@
6767
<Compile Include="SimpleTypeConversionTests.cs" />
6868
<Compile Include="TestBase.cs" />
6969
<Compile Include="TestHelpers.cs" />
70+
<Compile Include="TypeActivatorTests.cs" />
7071
</ItemGroup>
7172
<ItemGroup>
7273
<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
@@ -79,6 +79,11 @@ static Configuration()
7979
/// </summary>
8080
public static readonly List<ITypeConverter> TypeConverters = new List<ITypeConverter>();
8181

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

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

287317
#endregion Configuration

Slapper.AutoMapper/Slapper.AutoMapper.InternalHelpers.cs

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

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

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

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

0 commit comments

Comments
 (0)