Skip to content

Commit

Permalink
Refactor to Object Mapper (#30)
Browse files Browse the repository at this point in the history
* Add support for copyable attribute

* Correct observations

* Minor Change

* move ObjectCopier to Mapper

* Improvements in unit testing.

* Refactoring ObjectMapper

* Allow to use Dictionary<string, object> as source for mapping.

* Add Functional methods and reduce double check ObjectMapper

* Fixing suggestion from CR.

* Reduce PropertyTypeCache instances

* Fix build

* Add TryParseBasicType method

* Ignore RunInConsoleModeTest test for now

* Before to publish
  • Loading branch information
geoperez authored Sep 19, 2017
1 parent f22cabc commit 3b900c7
Show file tree
Hide file tree
Showing 31 changed files with 1,068 additions and 955 deletions.
10 changes: 4 additions & 6 deletions src/Unosquare.Swan/Abstractions/SettingsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,8 @@ public List<string> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList)
targetArray.SetValue(null, i++);
continue;
}

object itemvalue;
if (Definitions.BasicTypesInfo[elementType].TryParse(sourceElement.ToString(), out itemvalue))

if (elementType.TryParseBasicType(sourceElement.ToString(), out var itemvalue))
targetArray.SetValue(itemvalue, i++);
}
catch
Expand All @@ -140,9 +139,8 @@ public List<string> RefreshFromList(List<ExtendedPropertyInfo<T>> propertyList)
}
else
{
object propertyValue;
if (Definitions.BasicTypesInfo[propertyInfo.PropertyType].TryParse(property.Value.ToString(),
out propertyValue))
if (propertyInfo.PropertyType.TryParseBasicType(property.Value.ToString(),
out var propertyValue))
{
if (propertyValue.Equals(originalValue)) continue;

Expand Down
12 changes: 4 additions & 8 deletions src/Unosquare.Swan/Components/ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ public class ArgumentParser
{
private const char Dash = '-';

private static readonly PropertyTypeCache TypeCache = new PropertyTypeCache();

/// <summary>
/// Initializes a new instance of the <see cref="ArgumentParser"/> class.
/// </summary>
Expand Down Expand Up @@ -154,9 +152,7 @@ public bool ParseArguments<T>(IEnumerable<string> args, T instance)
}

private static IEnumerable<PropertyInfo> GetTypeProperties(Type type)
{
return TypeCache.Retrieve(type, PropertyTypeCache.GetAllPublicPropertiesFunc(type));
}
=> Runtime.PropertyTypeCache.Value.Retrieve(type, PropertyTypeCache.GetAllPublicPropertiesFunc(type));

private static void WriteUsage(IEnumerable<PropertyInfo> properties)
{
Expand Down Expand Up @@ -213,7 +209,7 @@ private bool SetPropertyValue<T>(PropertyInfo targetProperty, string propertyVal
{
if (primitiveValue)
{
if (Definitions.BasicTypesInfo[itemType].TryParse(value, out object itemvalue))
if (itemType.TryParseBasicType(value, out var itemvalue))
arr.SetValue(itemvalue, i++);
}
else
Expand All @@ -227,8 +223,8 @@ private bool SetPropertyValue<T>(PropertyInfo targetProperty, string propertyVal
return true;
}

if (Definitions.BasicTypesInfo[targetProperty.PropertyType].TryParse(propertyValueString,
out object propertyValue))
if (targetProperty.PropertyType.TryParseBasicType(propertyValueString,
out var propertyValue))
{
targetProperty.SetValue(result, propertyValue);
return true;
Expand Down
121 changes: 53 additions & 68 deletions src/Unosquare.Swan/Components/DependencyContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,28 +343,6 @@ public MultiRegisterOptions(IEnumerable<RegisterOptions> registerOptions)
_registerOptions = registerOptions;
}

/// <summary>
/// Make registration a singleton (single instance) if possible
/// </summary>
/// <returns>A registration multi-instance for fluent API</returns>
/// <exception cref="DependencyContainerRegistrationException">Generic Constraint Registration Exception</exception>
public MultiRegisterOptions AsSingleton()
{
_registerOptions = ExecuteOnAllRegisterOptions(ro => ro.AsSingleton());
return this;
}

/// <summary>
/// Make registration multi-instance if possible
/// </summary>
/// <returns>A registration multi-instance for fluent API</returns>
/// <exception cref="DependencyContainerRegistrationException">Generic Constraint Registration Exception</exception>
public MultiRegisterOptions AsMultiInstance()
{
_registerOptions = ExecuteOnAllRegisterOptions(ro => ro.AsMultiInstance());
return this;
}

/// <summary>
/// Switches to a custom lifetime manager factory if possible.
///
Expand Down Expand Up @@ -393,6 +371,28 @@ public static MultiRegisterOptions ToCustomLifetimeManager(
return instance;
}

/// <summary>
/// Make registration a singleton (single instance) if possible
/// </summary>
/// <returns>A registration multi-instance for fluent API</returns>
/// <exception cref="DependencyContainerRegistrationException">Generic Constraint Registration Exception</exception>
public MultiRegisterOptions AsSingleton()
{
_registerOptions = ExecuteOnAllRegisterOptions(ro => ro.AsSingleton());
return this;
}

/// <summary>
/// Make registration multi-instance if possible
/// </summary>
/// <returns>A registration multi-instance for fluent API</returns>
/// <exception cref="DependencyContainerRegistrationException">Generic Constraint Registration Exception</exception>
public MultiRegisterOptions AsMultiInstance()
{
_registerOptions = ExecuteOnAllRegisterOptions(ro => ro.AsMultiInstance());
return this;
}

private IEnumerable<RegisterOptions> ExecuteOnAllRegisterOptions(Func<RegisterOptions, RegisterOptions> action)
{
return _registerOptions.Select(action).ToList();
Expand Down Expand Up @@ -1824,9 +1824,7 @@ public TypeRegistration(Type type, string name = null)
/// </returns>
public override bool Equals(object obj)
{
var typeRegistration = obj as TypeRegistration;

if (typeRegistration == null || typeRegistration.Type != Type)
if (!(obj is TypeRegistration typeRegistration) || typeRegistration.Type != Type)
return false;

return string.Compare(Name, typeRegistration.Name, StringComparison.Ordinal) == 0;
Expand All @@ -1850,9 +1848,7 @@ private static readonly ConcurrentDictionary<ConstructorInfo, ObjectConstructor>
= new ConcurrentDictionary<ConstructorInfo, ObjectConstructor>();
#endif
#endregion

#region Constructors


/// <summary>
/// Initializes a new instance of the <see cref="DependencyContainer"/> class.
/// </summary>
Expand All @@ -1868,7 +1864,6 @@ private DependencyContainer(DependencyContainer parent)
{
_parent = parent;
}
#endregion

#region Internal Methods
private readonly object _autoRegisterLock = new object();
Expand Down Expand Up @@ -1991,9 +1986,7 @@ private void RegisterDefaultTypes()

private ObjectFactoryBase GetCurrentFactory(TypeRegistration registration)
{
ObjectFactoryBase current;

_registeredTypes.TryGetValue(registration, out current);
_registeredTypes.TryGetValue(registration, out var current);

return current;
}
Expand All @@ -2013,10 +2006,7 @@ private RegisterOptions AddUpdateRegistration(TypeRegistration typeRegistration,
}

private bool RemoveRegistration(TypeRegistration typeRegistration)
{
ObjectFactoryBase item;
return _registeredTypes.TryRemove(typeRegistration, out item);
}
=> _registeredTypes.TryRemove(typeRegistration, out var item);

private bool CanResolveInternal(TypeRegistration registration, DependencyContainerNamedParameterOverloads parameters, DependencyContainerResolveOptions options)
{
Expand All @@ -2026,8 +2016,7 @@ private bool CanResolveInternal(TypeRegistration registration, DependencyContain
var checkType = registration.Type;
var name = registration.Name;

ObjectFactoryBase factory;
if (_registeredTypes.TryGetValue(new TypeRegistration(checkType, name), out factory))
if (_registeredTypes.TryGetValue(new TypeRegistration(checkType, name), out var factory))
{
if (factory.AssumeConstruction)
return true;
Expand Down Expand Up @@ -2100,8 +2089,7 @@ private ObjectFactoryBase GetParentObjectFactory(TypeRegistration registration)
if (_parent == null)
return null;

ObjectFactoryBase factory;
if (_parent._registeredTypes.TryGetValue(registration, out factory))
if (_parent._registeredTypes.TryGetValue(registration, out var factory))
{
return factory.GetFactoryForChildContainer(registration.Type, _parent, this);
}
Expand All @@ -2111,10 +2099,8 @@ private ObjectFactoryBase GetParentObjectFactory(TypeRegistration registration)

private object ResolveInternal(TypeRegistration registration, DependencyContainerNamedParameterOverloads parameters, DependencyContainerResolveOptions options)
{
ObjectFactoryBase factory;

// Attempt container resolution
if (_registeredTypes.TryGetValue(registration, out factory))
if (_registeredTypes.TryGetValue(registration, out var factory))
{
try
{
Expand Down Expand Up @@ -2298,8 +2284,7 @@ private object ConstructType(Type implementationType, ConstructorInfo constructo
#if USE_OBJECT_CONSTRUCTOR
private static ObjectConstructor CreateObjectConstructionDelegateWithCache(ConstructorInfo constructor)
{
ObjectConstructor objectConstructor;
if (ObjectConstructorCache.TryGetValue(constructor, out objectConstructor))
if (ObjectConstructorCache.TryGetValue(constructor, out var objectConstructor))
return objectConstructor;

// We could lock the cache here, but there's no real side
Expand Down Expand Up @@ -2328,6 +2313,29 @@ private static ObjectConstructor CreateObjectConstructionDelegateWithCache(Const
}
#endif

private static bool IsValidAssignment(Type registerType, Type registerImplementation)
{
if (!registerType.IsGenericTypeDefinition())
{
if (!registerType.IsAssignableFrom(registerImplementation))
return false;
}
else
{
if (registerType.IsInterface())
{
if (registerImplementation.GetInterfaces().All(t => t.Name != registerType.Name))
return false;
}
else if (registerType.IsAbstract() && registerImplementation.BaseType() != registerType)
{
return false;
}
}

return true;
}

private void BuildUpInternal(object input, DependencyContainerResolveOptions resolveOptions)
{
var properties = from property in input.GetType().GetProperties()
Expand Down Expand Up @@ -2369,29 +2377,6 @@ private IEnumerable<object> ResolveAllInternal(Type resolveType, bool includeUnn
return registrations.Select(registration => ResolveInternal(registration, DependencyContainerNamedParameterOverloads.Default, DependencyContainerResolveOptions.Default));
}

private static bool IsValidAssignment(Type registerType, Type registerImplementation)
{
if (!registerType.IsGenericTypeDefinition())
{
if (!registerType.IsAssignableFrom(registerImplementation))
return false;
}
else
{
if (registerType.IsInterface())
{
if (registerImplementation.GetInterfaces().All(t => t.Name != registerType.Name))
return false;
}
else if (registerType.IsAbstract() && registerImplementation.BaseType() != registerType)
{
return false;
}
}

return true;
}

#endregion

#region IDisposable Members
Expand Down
67 changes: 67 additions & 0 deletions src/Unosquare.Swan/Components/EnumHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Provide Enumerations helpers with internal cache
/// </summary>
public static class EnumHelper
{
private static readonly Dictionary<Type, Tuple<int, string>[]> Cache =
new Dictionary<Type, Tuple<int, string>[]>();

private static readonly object LockObject = new object();

/// <summary>
/// Gets the items with the enum item value.
/// </summary>
/// <typeparam name="T">The type of enumeration</typeparam>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// A collection of Type/Tuple pairs
/// that represents items with the enum item value
/// </returns>
public static Tuple<int, string>[] GetItemsWithValue<T>(bool humanize = true)
{
lock (LockObject)
{
if (Cache.ContainsKey(typeof(T)) == false)
{
Cache.Add(typeof(T), Enum.GetNames(typeof(T))
.Select(x => new Tuple<int, string>((int)Enum.Parse(typeof(T), x), humanize ? x.Humanize() : x))
.ToArray());
}

return Cache[typeof(T)];
}
}

/// <summary>
/// Gets the items with the enum item index.
/// </summary>
/// <typeparam name="T">The type of enumeration</typeparam>
/// <param name="humanize">if set to <c>true</c> [humanize].</param>
/// <returns>
/// A collection of Type/Tuple pairs that represents items with the enum item value
/// </returns>
public static Tuple<int, string>[] GetItemsWithIndex<T>(bool humanize = true)
{
lock (LockObject)
{
if (Cache.ContainsKey(typeof(T)) == false)
{
var i = 0;

Cache.Add(typeof(T), Enum.GetNames(typeof(T))
.Select(x => new Tuple<int, string>(i++, humanize ? x.Humanize() : x))
.ToArray());
}

return Cache[typeof(T)];
}
}
}

}
27 changes: 27 additions & 0 deletions src/Unosquare.Swan/Components/IObjectMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Unosquare.Swan.Components
{
using System;
using System.Collections.Generic;
using System.Reflection;

/// <summary>
/// Interface object map
/// </summary>
public interface IObjectMap
{
/// <summary>
/// Gets or sets the map.
/// </summary>
Dictionary<PropertyInfo, List<PropertyInfo>> Map { get; }

/// <summary>
/// Gets or sets the type of the source.
/// </summary>
Type SourceType { get; }

/// <summary>
/// Gets or sets the type of the destination.
/// </summary>
Type DestinationType { get; }
}
}
2 changes: 1 addition & 1 deletion src/Unosquare.Swan/Components/MessageHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ void Publish<TMessage>(TMessage message)
/// </summary>
/// <typeparam name="TMessage">Type of message</typeparam>
/// <param name="message">Message to deliver</param>
/// <returns></returns>
/// <returns>A task from Publish action</returns>
Task PublishAsync<TMessage>(TMessage message)
where TMessage : class, IMessageHubMessage;
}
Expand Down
Loading

0 comments on commit 3b900c7

Please sign in to comment.