@ -15,6 +15,7 @@ using LightweightIocContainer.Interfaces.Installers;
using LightweightIocContainer.Interfaces.Registrations ;
using LightweightIocContainer.Interfaces.Registrations ;
using LightweightIocContainer.Interfaces.Registrations.Fluent ;
using LightweightIocContainer.Interfaces.Registrations.Fluent ;
using LightweightIocContainer.Registrations ;
using LightweightIocContainer.Registrations ;
using LightweightIocContainer.ResolvePlaceholders ;
namespace LightweightIocContainer
namespace LightweightIocContainer
{
{
@ -266,7 +267,24 @@ namespace LightweightIocContainer
/// <returns>An instance of the given <see cref="Type"/></returns>
/// <returns>An instance of the given <see cref="Type"/></returns>
/// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception>
/// <exception cref="InternalResolveException">Could not find function <see cref="ResolveInternal{T}"/></exception>
internal object Resolve ( Type type , object [ ] arguments , List < Type > resolveStack ) = >
internal object Resolve ( Type type , object [ ] arguments , List < Type > resolveStack ) = >
GenericMethodCaller . Call ( this , nameof ( ResolveInternal ) , type , BindingFlags . NonPublic | BindingFlags . Instance , arguments , resolveStack ) ;
GenericMethodCaller . CallPrivate ( this , nameof ( ResolveInternal ) , type , arguments , resolveStack ) ;
private object Resolve ( InternalToBeResolvedPlaceholder toBeResolvedPlaceholder , List < Type > resolveStack )
{
if ( toBeResolvedPlaceholder . Parameters = = null )
return Resolve ( toBeResolvedPlaceholder . ResolvedType , null , resolveStack ) ;
List < object > parameters = new ( ) ;
foreach ( object parameter in toBeResolvedPlaceholder . Parameters )
{
if ( parameter is InternalToBeResolvedPlaceholder internalToBeResolvedPlaceholder )
parameters . Add ( Resolve ( internalToBeResolvedPlaceholder , resolveStack ) ) ;
else
parameters . Add ( parameter ) ;
}
return Resolve ( toBeResolvedPlaceholder . ResolvedType , parameters . ToArray ( ) , resolveStack ) ;
}
/// <summary>
/// <summary>
/// Gets an instance of a given registered <see cref="Type"/>
/// Gets an instance of a given registered <see cref="Type"/>
@ -282,12 +300,7 @@ namespace LightweightIocContainer
IRegistration registration = FindRegistration < T > ( ) ? ? throw new TypeNotRegisteredException ( typeof ( T ) ) ;
IRegistration registration = FindRegistration < T > ( ) ? ? throw new TypeNotRegisteredException ( typeof ( T ) ) ;
//Circular dependency check
//Circular dependency check
if ( resolveStack = = null ) //first resolve call
resolveStack = CheckForCircularDependencies < T > ( resolveStack ) ;
resolveStack = new List < Type > { typeof ( T ) } ; //create new stack and add the currently resolving type to the stack
else if ( resolveStack . Contains ( typeof ( T ) ) )
throw new CircularDependencyException ( typeof ( T ) , resolveStack ) ; //currently resolving type is still resolving -> circular dependency
else //not the first resolve call in chain but no circular dependencies for now
resolveStack . Add ( typeof ( T ) ) ; //add currently resolving type to the stack
T resolvedInstance = registration switch
T resolvedInstance = registration switch
{
{
@ -313,15 +326,9 @@ namespace LightweightIocContainer
/// <returns>An existing or newly created singleton instance of the given <see cref="Type"/></returns>
/// <returns>An existing or newly created singleton instance of the given <see cref="Type"/></returns>
private T GetOrCreateSingletonInstance < T > ( IRegistration registration , object [ ] arguments , List < Type > resolveStack )
private T GetOrCreateSingletonInstance < T > ( IRegistration registration , object [ ] arguments , List < Type > resolveStack )
{
{
Type type = registration switch
Type type = GetType < T > ( registration ) ;
{
ITypedRegistration typedRegistration = > typedRegistration . ImplementationType ,
ISingleTypeRegistration < T > singleTypeRegistration = > singleTypeRegistration . InterfaceType ,
_ = > throw new UnknownRegistrationException ( $"There is no registration {registration.GetType().Name} that can have lifestyle singleton." )
} ;
//if a singleton instance exists return it
object instance = TryGetSingletonInstance ( type ) ;
object instance = _ singletons . FirstOrDefault ( s = > s . type = = type ) . instance ;
if ( instance ! = null )
if ( instance ! = null )
return ( T ) instance ;
return ( T ) instance ;
@ -332,6 +339,8 @@ namespace LightweightIocContainer
return newInstance ;
return newInstance ;
}
}
private object TryGetSingletonInstance ( Type type ) = > _ singletons . FirstOrDefault ( s = > s . type = = type ) . instance ; //if a singleton instance exists return it
/// <summary>
/// <summary>
/// Gets or creates a multiton instance of a given <see cref="Type"/>
/// Gets or creates a multiton instance of a given <see cref="Type"/>
/// </summary>
/// </summary>
@ -390,7 +399,7 @@ namespace LightweightIocContainer
T instance ;
T instance ;
if ( registration is IOpenGenericRegistration openGenericRegistration )
if ( registration is IOpenGenericRegistration openGenericRegistration )
{
{
arguments = ResolveConstructor Arguments ( openGenericRegistration . ImplementationType , arguments , resolveStack ) ;
arguments = ResolveTypeCreation Arguments ( openGenericRegistration . ImplementationType , arguments , resolveStack ) ;
//create generic implementation type from generic arguments of T
//create generic implementation type from generic arguments of T
Type genericImplementationType = openGenericRegistration . ImplementationType . MakeGenericType ( typeof ( T ) . GenericTypeArguments ) ;
Type genericImplementationType = openGenericRegistration . ImplementationType . MakeGenericType ( typeof ( T ) . GenericTypeArguments ) ;
@ -399,7 +408,7 @@ namespace LightweightIocContainer
}
}
else if ( registration is ITypedRegistration defaultRegistration )
else if ( registration is ITypedRegistration defaultRegistration )
{
{
arguments = ResolveConstructor Arguments ( defaultRegistration . ImplementationType , arguments , resolveStack ) ;
arguments = ResolveTypeCreation Arguments ( defaultRegistration . ImplementationType , arguments , resolveStack ) ;
instance = ( T ) Activator . CreateInstance ( defaultRegistration . ImplementationType , arguments ) ;
instance = ( T ) Activator . CreateInstance ( defaultRegistration . ImplementationType , arguments ) ;
}
}
else if ( registration is ISingleTypeRegistration < T > singleTypeRegistration )
else if ( registration is ISingleTypeRegistration < T > singleTypeRegistration )
@ -409,7 +418,7 @@ namespace LightweightIocContainer
if ( singleTypeRegistration . FactoryMethod = = null ) //type registration without interface -> just create this type
if ( singleTypeRegistration . FactoryMethod = = null ) //type registration without interface -> just create this type
{
{
arguments = ResolveConstructor Arguments ( singleTypeRegistration . InterfaceType , arguments , resolveStack ) ;
arguments = ResolveTypeCreation Arguments ( singleTypeRegistration . InterfaceType , arguments , resolveStack ) ;
instance = ( T ) Activator . CreateInstance ( singleTypeRegistration . InterfaceType , arguments ) ;
instance = ( T ) Activator . CreateInstance ( singleTypeRegistration . InterfaceType , arguments ) ;
}
}
else //factory method set to create the instance
else //factory method set to create the instance
@ -468,7 +477,7 @@ namespace LightweightIocContainer
}
}
/// <summary>
/// <summary>
/// Resolve the missing constructor arguments
/// Resolve the missing type creation arguments
/// </summary>
/// </summary>
/// <param name="type">The <see cref="Type"/> that will be created</param>
/// <param name="type">The <see cref="Type"/> that will be created</param>
/// <param name="arguments">The existing arguments</param>
/// <param name="arguments">The existing arguments</param>
@ -476,99 +485,185 @@ namespace LightweightIocContainer
/// <returns>An array of all needed constructor arguments to create the <see cref="Type"/></returns>
/// <returns>An array of all needed constructor arguments to create the <see cref="Type"/></returns>
/// <exception cref="NoMatchingConstructorFoundException">No matching constructor was found for the given or resolvable arguments</exception>
/// <exception cref="NoMatchingConstructorFoundException">No matching constructor was found for the given or resolvable arguments</exception>
[CanBeNull]
[CanBeNull]
private object [ ] ResolveConstructor Arguments ( Type type , object [ ] arguments , List < Type > resolveStack )
private object [ ] ResolveTypeCreation Arguments ( Type type , object [ ] arguments , List < Type > resolveStack )
{
{
NoMatchingConstructorFoundException noMatchingConstructorFoundException = null ;
//find best ctor
//find best ctor
List < ConstructorInfo > sortedConstructors = type . GetConstructors ( ) . OrderByDescending ( c = > c . GetParameters ( ) . Length ) . ToList ( ) ;
List < ConstructorInfo > sortedConstructors = TryGetSortedConstructors ( type ) ;
if ( ! sortedConstructors . Any ( ) ) //no public constructor available
foreach ( ConstructorInfo constructor in sortedConstructors )
throw new NoPublicConstructorFoundException ( type ) ;
{
( bool result , List < object > parameters , List < ConstructorNotMatchingException > exceptions ) = TryGetConstructorResolveStack ( constructor , arguments , resolveStack ) ;
NoMatchingConstructorFoundException noMatchingConstructorFoundException = null ;
if ( result )
{
if ( parameters = = null )
return null ;
foreach ( ConstructorInfo ctor in sortedConstructors )
List < object > constructorParameters = new ( ) ;
foreach ( object parameter in parameters )
{
{
try
if ( parameter is InternalToBeResolvedPlaceholder toBeResolvedPlaceholder )
constructorParameters . Add ( Resolve ( toBeResolvedPlaceholder , resolveStack ) ) ;
else
constructorParameters . Add ( parameter ) ;
}
return constructorParameters . ToArray ( ) ;
}
noMatchingConstructorFoundException ? ? = new NoMatchingConstructorFoundException ( type ) ;
exceptions . ForEach ( e = >
noMatchingConstructorFoundException . AddInnerException ( new ConstructorNotMatchingException ( constructor , e ) ) ) ;
}
if ( noMatchingConstructorFoundException ! = null )
throw noMatchingConstructorFoundException ;
return null ;
}
private ( bool result , List < object > parameters , List < ConstructorNotMatchingException > constructorNotMatchingExceptions ) TryGetConstructorResolveStack ( ConstructorInfo constructor , object [ ] arguments , List < Type > resolveStack )
{
{
List < object > argumentsList = arguments ? . ToList ( ) ;
List < ParameterInfo > constructorParameters = constructor . GetParameters ( ) . ToList ( ) ;
List < object > ctorParams = new ( ) ;
if ( ! constructorParameters . Any ( ) )
return ( true , null , null ) ;
List < ConstructorNotMatchingException > constructorNotMatchingExceptions = new ( ) ;
List < object > parameters = new ( ) ;
ParameterInfo [ ] parameters = ctor . GetParameters ( ) ;
List < object > passedArguments = null ;
foreach ( ParameterInfo parameter in parameters )
if ( arguments ! = null )
passedArguments = new List < object > ( arguments ) ;
foreach ( ParameterInfo parameter in constructorParameters )
{
{
object fittingArgument = new InternalResolvePlaceholder ( ) ;
object fittingArgument = new InternalResolvePlaceholder ( ) ;
if ( argumentsList ! = null )
if ( p assedA rguments ! = null )
{
{
fittingArgument = argumentsList . FirstOrGiven < object , InternalResolvePlaceholder > ( a = >
fittingArgument = p assedA rguments. FirstOrGiven < object , InternalResolvePlaceholder > ( a = >
a ? . GetType ( ) = = parameter . ParameterType | | parameter . ParameterType . IsInstanceOfType ( a ) ) ;
a ? . GetType ( ) = = parameter . ParameterType | | parameter . ParameterType . IsInstanceOfType ( a ) ) ;
if ( fittingArgument is not InternalResolvePlaceholder )
if ( fittingArgument is not InternalResolvePlaceholder )
{
{
int index = argumentsList . IndexOf ( fittingArgument ) ;
int index = passedArguments . IndexOf ( fittingArgument ) ; //todo
argumentsList [ index ] = new InternalResolvePlaceholder ( ) ;
passedArguments [ index ] = new InternalResolvePlaceholder ( ) ;
}
}
}
else //fittingArgument is InternalResolvePlaceholder
if ( fittingArgument is InternalResolvePlaceholder )
{
{
try
try
{
{
fittingArgument = Resolve ( parameter . ParameterType , null , resolveStack ) ;
IRegistration registration = FindRegistration ( parameter . ParameterType ) ? ? throw new TypeNotRegisteredException ( parameter . ParameterType ) ;
}
catch ( Exception )
List < Type > internalResolveStack = new ( resolveStack ) ;
internalResolveStack = CheckForCircularDependencies ( parameter . ParameterType , internalResolveStack ) ; //testMe: seems to work
Type registeredType = GetTypeNonGeneric ( parameter . ParameterType , registration ) ;
object singletonInstance = TryGetSingletonInstance ( registeredType ) ;
if ( singletonInstance ! = null )
fittingArgument = singletonInstance ;
else
{
{
fittingArgument = argumentsList . FirstOrGiven < object , InternalResolvePlaceholder > ( a = > parameter . ParameterType . GetDefault ( ) = = a ) ;
object [ ] argumentsForRegistration = null ;
if ( registration is IWithParametersInternal { Parameters : { } } registrationWithParameters )
argumentsForRegistration = UpdateArgumentsWithRegistrationParameters ( registrationWithParameters , null ) ;
if ( fittingArgument is not InternalResolvePlaceholder )
foreach ( ConstructorInfo registeredTypeConstructor in TryGetSortedConstructors ( registeredType ) )
{
( bool result , List < object > parametersToResolve , List < ConstructorNotMatchingException > exceptions ) =
TryGetConstructorResolveStack ( registeredTypeConstructor , argumentsForRegistration , internalResolveStack ) ;
if ( result )
{
{
int index = argumentsList . IndexOf ( fittingArgument ) ;
fittingArgument = new InternalToBeResolvedPlaceholder ( registeredType , parametersToResolve ) ;
argumentsList [ index ] = new InternalResolvePlaceholder ( ) ;
break ;
}
}
constructorNotMatchingExceptions . AddRange ( exceptions ) ;
}
}
}
}
}
}
catch ( Exception exception )
if ( fittingArgument is InternalResolvePlaceholder & & parameter . HasDefaultValue )
{
ctorParams . Add ( parameter . DefaultValue ) ;
constructorNotMatchingExceptions . Add ( new ConstructorNotMatchingException ( constructor , exception ) ) ;
else if ( fittingArgument is InternalResolvePlaceholder )
ctorParams . Add ( Resolve ( parameter . ParameterType , null , resolveStack ) ) ;
else
ctorParams . Add ( fittingArgument ) ;
}
}
return ctorParams . ToArray ( ) ;
if ( fittingArgument is InternalResolvePlaceholder & & passedArguments ! = null )
}
catch ( CircularDependencyException ) //don't handle circular dependencies as no matching constructor, just rethrow them
{
{
throw ;
fittingArgument = passedArguments . FirstOrGiven < object , InternalResolvePlaceholder > ( a = > parameter . ParameterType . GetDefault ( ) = = a ) ;
}
catch ( Exception ex )
if ( fittingArgument is not InternalResolvePlaceholder )
{
{
noMatchingConstructorFoundException ? ? = new NoMatchingConstructorFoundException ( type ) ;
int index = passedArguments . IndexOf ( fittingArgument ) ;
noMatchingConstructorFoundException . AddInnerException ( new ConstructorNotMatchingException ( ctor , ex ) ) ;
passedArguments [ index ] = new InternalResolvePlaceholder ( ) ;
}
}
}
}
}
if ( noMatchingConstructorFoundException ! = null )
if ( fittingArgument is InternalResolvePlaceholder & & parameter . HasDefaultValue )
throw noMatchingConstructorFoundException ;
parameters . Add ( parameter . DefaultValue ) ;
else
parameters . Add ( fittingArgument ) ;
}
return null ;
return ( ! parameters . Any ( p = > p is InternalResolvePlaceholder ) , parameters , constructorNotMatchingExceptions ) ;
}
}
[CanBeNull]
[CanBeNull]
private IRegistration FindRegistration < T > ( )
private IRegistration FindRegistration < T > ( ) = > FindRegistration ( typeof ( T ) ) ;
[CanBeNull]
private IRegistration FindRegistration ( Type type )
{
{
IRegistration registration = Registrations . FirstOrDefault ( r = > r . InterfaceType = = typeof ( T ) ) ;
IRegistration registration = Registrations . FirstOrDefault ( r = > r . InterfaceType = = type ) ;
if ( registration ! = null )
if ( registration ! = null )
return registration ;
return registration ;
registration = Registrations . OfType < ITypedRegistration > ( ) . FirstOrDefault ( r = > r . ImplementationType = = typeof ( T ) ) ;
registration = Registrations . OfType < ITypedRegistration > ( ) . FirstOrDefault ( r = > r . ImplementationType = = type ) ;
if ( registration ! = null )
if ( registration ! = null )
return registration ;
return registration ;
//check for open generic registration
//check for open generic registration
if ( ! typeof ( T ) . GenericTypeArguments . Any ( ) )
if ( ! type . GenericTypeArguments . Any ( ) )
return null ;
return null ;
List < IRegistration > openGenericRegistrations = Registrations . Where ( r = > r . InterfaceType . ContainsGenericParameters ) . ToList ( ) ;
List < IRegistration > openGenericRegistrations = Registrations . Where ( r = > r . InterfaceType . ContainsGenericParameters ) . ToList ( ) ;
return ! openGenericRegistrations . Any ( ) ? null : openGenericRegistrations . FirstOrDefault ( r = > r . InterfaceType = = typeof ( T ) . GetGenericTypeDefinition ( ) ) ;
return ! openGenericRegistrations . Any ( ) ? null : openGenericRegistrations . FirstOrDefault ( r = > r . InterfaceType = = type . GetGenericTypeDefinition ( ) ) ;
}
private List < ConstructorInfo > TryGetSortedConstructors ( Type type )
{
List < ConstructorInfo > sortedConstructors = type . GetConstructors ( ) . OrderByDescending ( c = > c . GetParameters ( ) . Length ) . ToList ( ) ;
if ( ! sortedConstructors . Any ( ) ) //no public constructor available
throw new NoPublicConstructorFoundException ( type ) ;
return sortedConstructors ;
}
private Type GetType < T > ( IRegistration registration ) = >
registration switch
{
ITypedRegistration typedRegistration = > typedRegistration . ImplementationType ,
ISingleTypeRegistration < T > singleTypeRegistration = > singleTypeRegistration . InterfaceType ,
_ = > throw new UnknownRegistrationException ( $"Unknown registration used: {registration.GetType().Name}." )
} ;
private Type GetTypeNonGeneric ( Type type , IRegistration registration ) = > ( Type ) GenericMethodCaller . CallPrivate ( this , nameof ( GetType ) , type , registration ) ;
private List < Type > CheckForCircularDependencies < T > ( List < Type > resolveStack ) = > CheckForCircularDependencies ( typeof ( T ) , resolveStack ) ;
private List < Type > CheckForCircularDependencies ( Type type , List < Type > resolveStack )
{
if ( resolveStack = = null ) //first resolve call
resolveStack = new List < Type > { type } ; //create new stack and add the currently resolving type to the stack
else if ( resolveStack . Contains ( type ) )
throw new CircularDependencyException ( type , resolveStack ) ; //currently resolving type is still resolving -> circular dependency
else //not the first resolve call in chain but no circular dependencies for now
resolveStack . Add ( type ) ; //add currently resolving type to the stack
return resolveStack ;
}
}
/// <summary>
/// <summary>