|
using System;
using System.Collections.Generic;
using System.Linq;
using BarbarianIOC.Enums;
using BarbarianIOC.Exceptions;
using BarbarianIOC.Factories;
using BarbarianIOC.Registration;
using System.Reflection;
using System.Linq.Expressions;
using BarbarianIOC.Attributes;
namespace BarbarianIOC
{
public class Container : IContainer
{
private object syncLock = new object();
private Dictionary<ComponentRegistration, IFactoryProvider> components =
new Dictionary<ComponentRegistration, IFactoryProvider>();
public void RegisterComponents(params ComponentRegistration[] registrations)
{
lock (syncLock)
{
foreach (ComponentRegistration componentRegistration in registrations.ToList())
{
components.Add(componentRegistration, null);
}
}
}
public void WireUp()
{
foreach (ComponentRegistration key in components.Where(c => c.Value == null).Select(c => c.Key).ToList())
{
CreateFactory(key, GetConstructorDelegateForType(key.TypeToCreate), key.InstanceMode);
}
}
public T Resolve<T>()
{
lock (syncLock)
{
IFactoryProvider creator = components.Where(x => x.Key.TypeToCreate == typeof(T)).Select(x => x.Value).SingleOrDefault();
if (creator != null)
{
T newlyCreatedObject = (T)creator.Create();
SatisfyProperties<T>(newlyCreatedObject);
return newlyCreatedObject;
}
else
{
throw new ContainerConfigurationException(string.Format(
"Couldn't create instance of {0} could not find correct IFactoryProvider. This may be down to missing Component registration",
typeof(T).FullName));
}
}
}
private void SatisfyProperties<T>(T newlyCreatedObject)
{
foreach (PropertyInfo prop in newlyCreatedObject.GetType().GetProperties()
.Where(x => x.GetCustomAttributes(typeof(DependencyAttribute), false).Count() > 0))
{
IFactoryProvider factoryProvider = components.Single(x => x.Key.TypeToCreate == prop.PropertyType ||
prop.PropertyType.IsAssignableFrom(x.Key.TypeToLookFor)).Value;
if (factoryProvider != null)
{
prop.SetValue(newlyCreatedObject, factoryProvider.Create(), null);
}
else
{
throw new ContainerConfigurationException(string.Format(
"Couldn't find instance of {0} to use for property injection", prop.PropertyType.FullName));
}
}
}
private Delegate GetConstructorDelegateForType(Type type)
{
ComponentRegistration componentRegistration = null;
//look for constructor that is marked with DependencyConstructorAttribute,
//if there is none just try and go for the default constructor
ConstructorInfo ctor = type.GetConstructors()
.Where(x => x.GetCustomAttributes(typeof(DependencyConstructorAttribute), false).Count() > 0).SingleOrDefault();
if (ctor == null)
{
ctor = type.GetConstructors()[0];
}
foreach (var ctorArg in ctor.GetParameters())
{
bool isParamCoveredByManualRegistration = IsParamCoveredByManualRegistration(type, ctorArg);
if (!isParamCoveredByManualRegistration)
{
bool parameterKeyFound = components.Keys.Any(x => x.TypeToCreate == ctorArg.ParameterType ||
ctorArg.ParameterType.IsAssignableFrom(x.TypeToLookFor));
if (!parameterKeyFound)
{
throw new ContainerConfigurationException(string.Format("Couldn't find ctor argument {0}", ctorArg.GetType()));
}
else
{
componentRegistration = FetchComponentRegistration(ctorArg.ParameterType);
if (components[componentRegistration] == null)
{
Delegate delegateForType = GetConstructorDelegateForType(componentRegistration.TypeToCreate);
CreateFactory(componentRegistration, delegateForType, componentRegistration.InstanceMode);
}
}
}
}
List<ConstructorParameterDependency> args = new List<ConstructorParameterDependency>();
foreach (var ctorArg in ctor.GetParameters())
{
bool isParamCoveredByManualRegistration = IsParamCoveredByManualRegistration(type, ctorArg);
if (!isParamCoveredByManualRegistration)
{
componentRegistration = FetchComponentRegistration(ctorArg.ParameterType);
args.Add(new ConstructorParameterDependency(
ctorArg.ParameterType,
ctorArg.Name,
Expression.Constant(components[componentRegistration].Create()),
ctorArg.Position));
}
}
componentRegistration = FetchComponentRegistration(type);
if (componentRegistration != null)
{
if (componentRegistration.DependsOnValues.Any())
{
args.AddRange(componentRegistration.DependsOnValues);
}
}
return Expression.Lambda(Expression.New(ctor, args.OrderBy(x => x.Position)
.Select(x => x.Value).ToArray())).Compile();
}
private ComponentRegistration FetchComponentRegistration(Type typeToLookFor)
{
ComponentRegistration componentRegistration =
components.Single(x => x.Key.TypeToCreate == typeToLookFor ||
typeToLookFor.IsAssignableFrom(x.Key.TypeToLookFor)).Key;
return componentRegistration;
}
private bool IsParamCoveredByManualRegistration(Type constructorOwnerType, ParameterInfo constructorArg)
{
ComponentRegistration componentRegistration = FetchComponentRegistration(constructorOwnerType);
if (!componentRegistration.HasManualConstructorParameters)
{
return false;
}
else
{
ConstructorParameterDependency constructorParameterDependency =
componentRegistration.DependsOnValues.SingleOrDefault(
x => x.ArgType == constructorArg.ParameterType && x.Name == constructorArg.Name);
if (constructorParameterDependency != null)
{
constructorParameterDependency.Position = constructorArg.Position;
return true;
}
else
{
return false;
}
}
}
private void CreateFactory(ComponentRegistration key, Delegate @delegate, InstanceMode instanceMode)
{
IFactoryProvider factoryProvider = null;
if (instanceMode == InstanceMode.Transient)
{
factoryProvider = new TransientFactory(@delegate);
}
if (instanceMode == InstanceMode.Singleton)
{
factoryProvider = new SingletonFactory(@delegate);
}
lock (syncLock)
{
components[key] = factoryProvider;
}
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
Both of these at Sussex University UK.
Award(s)
I am lucky enough to have won a few awards for Zany Crazy code articles over the years
- Microsoft C# MVP 2016
- Codeproject MVP 2016
- Microsoft C# MVP 2015
- Codeproject MVP 2015
- Microsoft C# MVP 2014
- Codeproject MVP 2014
- Microsoft C# MVP 2013
- Codeproject MVP 2013
- Microsoft C# MVP 2012
- Codeproject MVP 2012
- Microsoft C# MVP 2011
- Codeproject MVP 2011
- Microsoft C# MVP 2010
- Codeproject MVP 2010
- Microsoft C# MVP 2009
- Codeproject MVP 2009
- Microsoft C# MVP 2008
- Codeproject MVP 2008
- And numerous codeproject awards which you can see over at my blog