Created
August 12, 2016 12:24
-
-
Save japf/62dcd40f3dab1e9956b21dfa1e2401d0 to your computer and use it in GitHub Desktop.
A helper class that can create object instance that have dependencies to interface by guessing class that implement those interfaces.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Diagnostics.CodeAnalysis; | |
using System.Globalization; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using Microsoft.Practices.Unity; | |
namespace MyApp.Tools | |
{ | |
/// <summary> | |
/// An helper class that is smart enough to create an instance of an object from its interface - automatically | |
/// It works by finding all types that implements an interface and have a public constructor. By using reflection | |
/// we find out what parameters are needed to call the constructor and we recursively create all instances. | |
/// </summary> | |
/// <remarks> | |
/// This class is NOT intended to be used in production environment but rather in tools or integration tests. | |
/// </remarks> | |
[ExcludeFromCodeCoverage] | |
public static class SmartObjectBuilder | |
{ | |
private const string AssemblyFilter = "YourCustomNamespace"; | |
private static Dictionary<Type, Type> implementations; // map interface type to implementation type (ex: <IFoo, Foo>) | |
private static Dictionary<Type, object> instances; // map interface type to actual implementation instances (ex: <IFoo, instance of object implementing IFoo>) | |
public static Action<string> LogHandler { get; set; } | |
/// <summary> | |
/// Create a new instance of a given type from its interface by dynamically inspecting its constructor and recursively fill | |
/// all parameters with their appropriate implementation. If the given type is an interface, the method automatically finds | |
/// a type that implements it and use it as type to create. | |
/// </summary> | |
/// <typeparam name="T">Type (concrete of interface) to create</typeparam> | |
/// <returns>An instance of the newly instanced object</returns> | |
public static T Build<T>() where T : class | |
{ | |
EnsureCacheIsBuilt(); | |
var type = typeof(T); | |
if (!type.IsInterface) | |
{ | |
ConstructorInfo constructor = type.GetConstructors()[0]; | |
List<object> parameters = new List<object>(); | |
foreach (var parameterInfo in constructor.GetParameters()) | |
{ | |
if (!parameterInfo.ParameterType.IsInterface) | |
{ | |
throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Could not instantiate constructor because parameter {0} is not an interface", parameterInfo.ParameterType.Name)); | |
} | |
if (!instances.ContainsKey(parameterInfo.ParameterType)) | |
{ | |
Build(parameterInfo.ParameterType); | |
} | |
parameters.Add(instances[parameterInfo.ParameterType]); | |
} | |
return (T)constructor.Invoke(parameters.ToArray()); | |
} | |
else | |
{ | |
Build(type); | |
return (T)instances[type]; | |
} | |
} | |
private static void Build(Type interfaceType) | |
{ | |
EnsureCacheIsBuilt(); | |
if (!interfaceType.IsInterface) | |
{ | |
throw new NotSupportedException("This method can only build interface type"); | |
} | |
if (!implementations.ContainsKey(interfaceType)) | |
{ | |
throw new NotSupportedException("This type is not known"); | |
} | |
if (instances.ContainsKey(interfaceType)) | |
{ | |
return; | |
} | |
if (UnityContainer.Instance.IsRegistered(interfaceType)) | |
{ | |
instances.Add(interfaceType, UnityContainer.Instance.Resolve(interfaceType)); | |
return; | |
} | |
Type concreteType = implementations[interfaceType]; | |
ConstructorInfo constructor = concreteType.GetConstructors()[0]; | |
List<object> parameters = new List<object>(); | |
foreach (var parameterInfo in constructor.GetParameters()) | |
{ | |
var parameterType = parameterInfo.ParameterType; | |
if (!instances.ContainsKey(parameterType)) | |
{ | |
Build(parameterType); | |
} | |
parameters.Add(instances[parameterType]); | |
} | |
if (LogHandler != null) | |
{ | |
LogHandler(string.Format(CultureInfo.InvariantCulture, "Building {0}", interfaceType.Name)); | |
} | |
object instance = constructor.Invoke(parameters.ToArray()); | |
instances.Add(interfaceType, instance); | |
if (!UnityContainer.Instance.IsRegistered(interfaceType)) | |
{ | |
UnityContainer.Instance.RegisterInstance(interfaceType, instance); | |
} | |
} | |
private static void EnsureCacheIsBuilt() | |
{ | |
if (implementations != null && instances != null) | |
{ | |
return; | |
} | |
try | |
{ | |
BuildCache(); | |
} | |
catch (Exception ex) | |
{ | |
Debug.WriteLine("Failed to build cache: " + ex); | |
throw; | |
} | |
} | |
private static void BuildCache() | |
{ | |
implementations = new Dictionary<Type, Type>(); | |
instances = new Dictionary<Type, object>(); | |
foreach (string path in Directory.GetFiles(".", "*.dll").Where(p => p.Contains(AssemblyFilter))) | |
{ | |
var assembly = Assembly.LoadFrom(path); | |
var types = assembly.GetTypes().Where(t => t.IsClass && t.GetInterfaces().Length > 0 && t.GetConstructors().Length == 1).ToList(); | |
foreach (Type type in types) | |
{ | |
foreach (var interfaceType in type.GetInterfaces()) | |
{ | |
ConstructorInfo constructor = type.GetConstructors().FirstOrDefault(c => c.GetParameters().All(p => p.ParameterType.IsInterface)); | |
if (constructor != null && !implementations.ContainsKey(interfaceType)) | |
{ | |
implementations.Add(interfaceType, type); | |
} | |
} | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment