diff --git a/Slapper.AutoMapper.Tests/Slapper.Tests.csproj b/Slapper.AutoMapper.Tests/Slapper.Tests.csproj
index b8c1172..d72260c 100644
--- a/Slapper.AutoMapper.Tests/Slapper.Tests.csproj
+++ b/Slapper.AutoMapper.Tests/Slapper.Tests.csproj
@@ -66,6 +66,7 @@
+
diff --git a/Slapper.AutoMapper.Tests/TypeActivatorTests.cs b/Slapper.AutoMapper.Tests/TypeActivatorTests.cs
new file mode 100644
index 0000000..5abf306
--- /dev/null
+++ b/Slapper.AutoMapper.Tests/TypeActivatorTests.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+
+namespace Slapper.Tests
+{
+ public class TypeActivatorTests
+ {
+ public interface IClass
+ {
+ string Id { get; set; }
+
+ INestedClass Nested { get; set; }
+ }
+
+ public class Class : IClass
+ {
+ public string Id { get; set; }
+
+ public INestedClass Nested { get; set; }
+ }
+
+ public interface INestedClass
+ {
+ int Count { get; set; }
+
+ Enum Enum { get; set; }
+ }
+
+ public class NestedClass : INestedClass
+ {
+ public int Count { get; set; }
+
+ public Enum Enum { get; set; }
+ }
+
+ public enum Enum
+ {
+ One,
+ Two
+ }
+
+ public class TypeActivator : AutoMapper.Configuration.ITypeActivator
+ {
+ private readonly IDictionary supportedTypes = new Dictionary
+ {
+ { typeof(IClass), typeof(Class) },
+ { typeof(INestedClass), typeof(NestedClass) }
+ };
+
+ public object Create(Type type)
+ {
+ var concreteType = this.supportedTypes[type];
+ return Activator.CreateInstance(concreteType);
+ }
+
+ public bool CanCreate(Type type)
+ {
+ return this.supportedTypes.ContainsKey(type);
+ }
+
+ public int Order { get; } = 1;
+ }
+
+ [Test]
+ public void Can_Use_Registered_TypeActivators_WithInterfaces()
+ {
+ // Arrange
+ const string id = "1";
+ const int count = 2;
+ const Enum enu = Enum.Two;
+
+ var dictionary = new Dictionary
+ {
+ { "Id", id },
+ { "Nested_Count", count },
+ { "Nested_Enum", enu }
+ };
+
+ AutoMapper.Configuration.TypeActivators.Add(new TypeActivator());
+
+ // Act
+ var result = Slapper.AutoMapper.Map(dictionary);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.That(result.Id == id);
+ Assert.That(result.Nested.Enum == enu);
+ Assert.That(result.Nested.Count == count);
+ }
+
+ public interface IClass2
+ {
+ string Id { get; set; }
+ }
+
+ public class Class2 : IClass2
+ {
+ public string Id { get; set; }
+ }
+
+ public class Class2Copy : IClass2
+ {
+ public string Id { get; set; }
+ }
+
+ public class Class2TypeActivator : AutoMapper.Configuration.ITypeActivator
+ {
+ public object Create(Type type)
+ {
+ return new Class2();
+ }
+
+ public bool CanCreate(Type type)
+ {
+ return type.IsAssignableFrom(typeof(IClass2));
+ }
+
+ public int Order { get; } = 2;
+ }
+
+ public class Class2CopyTypeActivator : AutoMapper.Configuration.ITypeActivator
+ {
+ public object Create(Type type)
+ {
+ return new Class2Copy();
+ }
+
+ public bool CanCreate(Type type)
+ {
+ return type.IsAssignableFrom(typeof(IClass2));
+ }
+
+ public int Order { get; } = 1;
+ }
+
+ [Test]
+ public void TypeActivator_Order_Is_Adhered_To()
+ {
+ // Arrange
+ const string id = "1";
+ const int count = 2;
+ const Enum enu = Enum.Two;
+
+ var dictionary = new Dictionary
+ {
+ { "Id", id },
+ { "Nested_Count", count },
+ { "Nested_Enum", enu }
+ };
+
+ AutoMapper.Configuration.TypeActivators.Add(new Class2TypeActivator());
+ AutoMapper.Configuration.TypeActivators.Add(new Class2CopyTypeActivator());
+
+ // Act
+ var result = Slapper.AutoMapper.Map(dictionary);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.That(result.Id == id);
+ Assert.That(result.GetType() == typeof(Class2Copy));
+ }
+ }
+}
diff --git a/Slapper.AutoMapper/Slapper.AutoMapper.Configuration.cs b/Slapper.AutoMapper/Slapper.AutoMapper.Configuration.cs
index 6c951a5..405c420 100644
--- a/Slapper.AutoMapper/Slapper.AutoMapper.Configuration.cs
+++ b/Slapper.AutoMapper/Slapper.AutoMapper.Configuration.cs
@@ -78,6 +78,11 @@ static Configuration()
///
public static readonly List TypeConverters = new List();
+ ///
+ /// Activators to instantiate types.
+ ///
+ public static readonly List TypeActivators = new List();
+
///
/// Applies default conventions for finding identifiers.
///
@@ -281,6 +286,31 @@ public bool CanConvert(object value, Type type)
#endregion
}
+
+ ///
+ /// Defines an interface for an activator for a specific type.
+ ///
+ public interface ITypeActivator
+ {
+ ///
+ /// Creates the type.
+ ///
+ /// The type to create.
+ /// The created type.
+ object Create(Type type);
+
+ ///
+ /// Indicates whether it can create the type.
+ ///
+ /// The type to create.
+ /// Boolean response.
+ bool CanCreate(Type type);
+
+ ///
+ /// The order to try the activator in.
+ ///
+ int Order { get; }
+ }
}
#endregion Configuration
diff --git a/Slapper.AutoMapper/Slapper.AutoMapper.InternalHelpers.cs b/Slapper.AutoMapper/Slapper.AutoMapper.InternalHelpers.cs
index aebb965..54f7a0f 100644
--- a/Slapper.AutoMapper/Slapper.AutoMapper.InternalHelpers.cs
+++ b/Slapper.AutoMapper/Slapper.AutoMapper.InternalHelpers.cs
@@ -88,7 +88,23 @@ public static Dictionary GetFieldsAndProperties(Type type)
///
public static object CreateInstance(Type type)
{
- return type == typeof(string) ? string.Empty : Activator.CreateInstance(type);
+ if (type == typeof(string))
+ {
+ return string.Empty;
+ }
+
+ if (Configuration.TypeActivators.Count > 0)
+ {
+ foreach (var typeActivator in Configuration.TypeActivators.OrderBy(ta => ta.Order))
+ {
+ if (typeActivator.CanCreate(type))
+ {
+ return typeActivator.Create(type);
+ }
+ }
+ }
+
+ return Activator.CreateInstance(type);
}
///
@@ -466,8 +482,12 @@ internal static object Map(IDictionary dictionary, object instan
// hasn't been initialized, then this will return null.
object nestedInstance = GetMemberValue(member, instance);
- // If the member is null and is a class, try to create an instance of the type
- if (nestedInstance == null && memberType.IsClass)
+ var genericCollectionType = typeof(IEnumerable<>);
+ var isEnumerableType = memberType.IsGenericType && genericCollectionType.IsAssignableFrom(memberType.GetGenericTypeDefinition())
+ || memberType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericCollectionType);
+
+ // If the member is null and is a class or interface (not ienumerable), try to create an instance of the type
+ if (nestedInstance == null && (memberType.IsClass || (memberType.IsInterface && !isEnumerableType)))
{
if (memberType.IsArray)
{
@@ -475,14 +495,13 @@ internal static object Map(IDictionary dictionary, object instan
}
else
{
- nestedInstance = typeof(IEnumerable).IsAssignableFrom(memberType) ? CreateInstance(memberType) : GetInstance(memberType, newDictionary, parentInstance == null ? 0 : parentInstance.GetHashCode()).Item2;
+ nestedInstance = typeof(IEnumerable).IsAssignableFrom(memberType)
+ ? CreateInstance(memberType)
+ : GetInstance(memberType, newDictionary, parentInstance == null ? 0 : parentInstance.GetHashCode()).Item2;
}
}
- Type genericCollectionType = typeof(IEnumerable<>);
-
- if (memberType.IsGenericType && genericCollectionType.IsAssignableFrom(memberType.GetGenericTypeDefinition())
- || memberType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericCollectionType))
+ if (isEnumerableType)
{
var innerType = memberType.GetGenericArguments().FirstOrDefault() ?? memberType.GetElementType();
nestedInstance = MapCollection(innerType, newDictionary, nestedInstance, instance);
diff --git a/Slapper.AutoMapper/Slapper.AutoMapper.cs b/Slapper.AutoMapper/Slapper.AutoMapper.cs
index cbf1f71..e15c5c5 100644
--- a/Slapper.AutoMapper/Slapper.AutoMapper.cs
+++ b/Slapper.AutoMapper/Slapper.AutoMapper.cs
@@ -65,7 +65,7 @@ public class Id : Attribute
#region Mapping
///
- /// Converts a list of dynamic property names and values to a list of type of .
+ /// Converts a dynamic object to a type .
///
/// Population of complex nested child properties is supported by underscoring "_" into the
/// nested child properties in the property name.
@@ -73,25 +73,43 @@ public class Id : Attribute
/// Type to instantiate and automap to
/// Dynamic list of property names and values
/// If false, clears instance cache after mapping is completed. Defaults to true, meaning instances are kept between calls.
- /// List of type
+ /// The type
/// Exception that is thrown when the cannot be converted to an IDictionary of type string and object.
public static T MapDynamic( object dynamicObject, bool keepCache = true)
{
- if ( dynamicObject == null )
- return default( T );
+ return (T)MapDynamic(typeof(T), dynamicObject, keepCache);
+ }
+
+ ///
+ /// Converts a dynamic object to a specified Type.
+ ///
+ /// Population of complex nested child properties is supported by underscoring "_" into the
+ /// nested child properties in the property name.
+ ///
+ /// Type to instantiate and automap to
+ /// Dynamic list of property names and values
+ /// If false, clears instance cache after mapping is completed. Defaults to true, meaning instances are kept between calls.
+ /// The specified Type
+ /// Exception that is thrown when the cannot be converted to an IDictionary of type string and object.
+ public static object MapDynamic(Type type, object dynamicObject, bool keepCache = true)
+ {
+ if (dynamicObject == null)
+ {
+ return type.IsValueType ? Activator.CreateInstance(type) : null;
+ }
var dictionary = dynamicObject as IDictionary;
- if ( dictionary == null )
- throw new ArgumentException( "Object type cannot be converted to an IDictionary", "dynamicObject" );
+ if (dictionary == null)
+ throw new ArgumentException("Object type cannot be converted to an IDictionary", "dynamicObject");
var propertiesList = new List> { dictionary };
- return Map( propertiesList, keepCache ).FirstOrDefault();
+ return Map(type, propertiesList, keepCache).FirstOrDefault();
}
///
- /// Converts a list of dynamic property names and values to a list of type of .
+ /// Converts a list of dynamic objects to a list of type of .
///
/// Population of complex nested child properties is supported by underscoring "_" into the
/// nested child properties in the property name.
@@ -103,22 +121,38 @@ public static T MapDynamic( object dynamicObject, bool keepCache = true)
/// Exception that is thrown when the cannot be converted to an IDictionary of type string and object.
public static IEnumerable MapDynamic( IEnumerable