Strip Swan library Part 1

This commit is contained in:
2019-12-09 17:25:54 +01:00
parent d0b26111dd
commit f1c7a29b38
73 changed files with 11356 additions and 0 deletions
@@ -0,0 +1,575 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Swan.DependencyInjection {
/// <summary>
/// The concrete implementation of a simple IoC container
/// based largely on TinyIoC (https://github.com/grumpydev/TinyIoC).
/// </summary>
/// <seealso cref="System.IDisposable" />
public partial class DependencyContainer : IDisposable {
private readonly Object _autoRegisterLock = new Object();
private Boolean _disposed;
static DependencyContainer() {
}
/// <summary>
/// Initializes a new instance of the <see cref="DependencyContainer"/> class.
/// </summary>
public DependencyContainer() {
this.RegisteredTypes = new TypesConcurrentDictionary(this);
_ = this.Register(this);
}
private DependencyContainer(DependencyContainer parent) : this() => this.Parent = parent;
/// <summary>
/// Lazy created Singleton instance of the container for simple scenarios.
/// </summary>
public static DependencyContainer Current { get; } = new DependencyContainer();
internal DependencyContainer Parent {
get;
}
internal TypesConcurrentDictionary RegisteredTypes {
get;
}
/// <inheritdoc />
public void Dispose() {
if(this._disposed) {
return;
}
this._disposed = true;
foreach(IDisposable disposable in this.RegisteredTypes.Values.Select(item => item as IDisposable)) {
disposable?.Dispose();
}
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets the child container.
/// </summary>
/// <returns>A new instance of the <see cref="DependencyContainer"/> class.</returns>
public DependencyContainer GetChildContainer() => new DependencyContainer(this);
#region Registration
/// <summary>
/// Attempt to automatically register all non-generic classes and interfaces in the current app domain.
/// Types will only be registered if they pass the supplied registration predicate.
/// </summary>
/// <param name="duplicateAction">What action to take when encountering duplicate implementations of an interface/base class.</param>
/// <param name="registrationPredicate">Predicate to determine if a particular type should be registered.</param>
public void AutoRegister(DependencyContainerDuplicateImplementationAction duplicateAction = DependencyContainerDuplicateImplementationAction.RegisterSingle, Func<Type, Boolean> registrationPredicate = null) => this.AutoRegister(AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)), duplicateAction, registrationPredicate);
/// <summary>
/// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies
/// Types will only be registered if they pass the supplied registration predicate.
/// </summary>
/// <param name="assemblies">Assemblies to process.</param>
/// <param name="duplicateAction">What action to take when encountering duplicate implementations of an interface/base class.</param>
/// <param name="registrationPredicate">Predicate to determine if a particular type should be registered.</param>
public void AutoRegister(IEnumerable<Assembly> assemblies, DependencyContainerDuplicateImplementationAction duplicateAction = DependencyContainerDuplicateImplementationAction.RegisterSingle, Func<Type, Boolean> registrationPredicate = null) {
lock(this._autoRegisterLock) {
List<Type> types = assemblies.SelectMany(a => a.GetAllTypes()).Where(t => !IsIgnoredType(t, registrationPredicate)).ToList();
List<Type> concreteTypes = types.Where(type => type.IsClass && !type.IsAbstract && type != this.GetType() && type.DeclaringType != this.GetType() && !type.IsGenericTypeDefinition).ToList();
foreach(Type type in concreteTypes) {
try {
_ = this.RegisteredTypes.Register(type, String.Empty, GetDefaultObjectFactory(type, type));
} catch(MethodAccessException) {
// Ignore methods we can't access - added for Silverlight
}
}
IEnumerable<Type> abstractInterfaceTypes = types.Where(type => (type.IsInterface || type.IsAbstract) && type.DeclaringType != this.GetType() && !type.IsGenericTypeDefinition);
foreach(Type type in abstractInterfaceTypes) {
Type localType = type;
List<Type> implementations = concreteTypes.Where(implementationType => localType.IsAssignableFrom(implementationType)).ToList();
if(implementations.Skip(1).Any()) {
if(duplicateAction == DependencyContainerDuplicateImplementationAction.Fail) {
throw new DependencyContainerRegistrationException(type, implementations);
}
if(duplicateAction == DependencyContainerDuplicateImplementationAction.RegisterMultiple) {
_ = this.RegisterMultiple(type, implementations);
}
}
Type firstImplementation = implementations.FirstOrDefault();
if(firstImplementation == null) {
continue;
}
try {
_ = this.RegisteredTypes.Register(type, String.Empty, GetDefaultObjectFactory(type, firstImplementation));
} catch(MethodAccessException) {
// Ignore methods we can't access - added for Silverlight
}
}
}
}
/// <summary>
/// Creates/replaces a named container class registration with default options.
/// </summary>
/// <param name="registerType">Type to register.</param>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register(Type registerType, String name = "") => this.RegisteredTypes.Register(registerType, name, GetDefaultObjectFactory(registerType, registerType));
/// <summary>
/// Creates/replaces a named container class registration with a given implementation and default options.
/// </summary>
/// <param name="registerType">Type to register.</param>
/// <param name="registerImplementation">Type to instantiate that implements RegisterType.</param>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register(Type registerType, Type registerImplementation, String name = "") => this.RegisteredTypes.Register(registerType, name, GetDefaultObjectFactory(registerType, registerImplementation));
/// <summary>
/// Creates/replaces a named container class registration with a specific, strong referenced, instance.
/// </summary>
/// <param name="registerType">Type to register.</param>
/// <param name="instance">Instance of RegisterType to register.</param>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register(Type registerType, Object instance, String name = "") => this.RegisteredTypes.Register(registerType, name, new InstanceFactory(registerType, registerType, instance));
/// <summary>
/// Creates/replaces a named container class registration with a specific, strong referenced, instance.
/// </summary>
/// <param name="registerType">Type to register.</param>
/// <param name="registerImplementation">Type of instance to register that implements RegisterType.</param>
/// <param name="instance">Instance of RegisterImplementation to register.</param>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register(Type registerType, Type registerImplementation, Object instance, String name = "") => this.RegisteredTypes.Register(registerType, name, new InstanceFactory(registerType, registerImplementation, instance));
/// <summary>
/// Creates/replaces a container class registration with a user specified factory.
/// </summary>
/// <param name="registerType">Type to register.</param>
/// <param name="factory">Factory/lambda that returns an instance of RegisterType.</param>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register(Type registerType, Func<DependencyContainer, Dictionary<String, Object>, Object> factory, String name = "") => this.RegisteredTypes.Register(registerType, name, new DelegateFactory(registerType, factory));
/// <summary>
/// Creates/replaces a named container class registration with default options.
/// </summary>
/// <typeparam name="TRegister">Type to register.</typeparam>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register<TRegister>(String name = "") where TRegister : class => this.Register(typeof(TRegister), name);
/// <summary>
/// Creates/replaces a named container class registration with a given implementation and default options.
/// </summary>
/// <typeparam name="TRegister">Type to register.</typeparam>
/// <typeparam name="TRegisterImplementation">Type to instantiate that implements RegisterType.</typeparam>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register<TRegister, TRegisterImplementation>(String name = "") where TRegister : class where TRegisterImplementation : class, TRegister => this.Register(typeof(TRegister), typeof(TRegisterImplementation), name);
/// <summary>
/// Creates/replaces a named container class registration with a specific, strong referenced, instance.
/// </summary>
/// <typeparam name="TRegister">Type to register.</typeparam>
/// <param name="instance">Instance of RegisterType to register.</param>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register<TRegister>(TRegister instance, String name = "") where TRegister : class => this.Register(typeof(TRegister), instance, name);
/// <summary>
/// Creates/replaces a named container class registration with a specific, strong referenced, instance.
/// </summary>
/// <typeparam name="TRegister">Type to register.</typeparam>
/// <typeparam name="TRegisterImplementation">Type of instance to register that implements RegisterType.</typeparam>
/// <param name="instance">Instance of RegisterImplementation to register.</param>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register<TRegister, TRegisterImplementation>(TRegisterImplementation instance, String name = "") where TRegister : class where TRegisterImplementation : class, TRegister => this.Register(typeof(TRegister), typeof(TRegisterImplementation), instance, name);
/// <summary>
/// Creates/replaces a named container class registration with a user specified factory.
/// </summary>
/// <typeparam name="TRegister">Type to register.</typeparam>
/// <param name="factory">Factory/lambda that returns an instance of RegisterType.</param>
/// <param name="name">Name of registration.</param>
/// <returns>RegisterOptions for fluent API.</returns>
public RegisterOptions Register<TRegister>(Func<DependencyContainer, Dictionary<String, Object>, TRegister> factory, String name = "") where TRegister : class {
if(factory == null) {
throw new ArgumentNullException(nameof(factory));
}
return this.Register(typeof(TRegister), factory, name);
}
/// <summary>
/// Register multiple implementations of a type.
///
/// Internally this registers each implementation using the full name of the class as its registration name.
/// </summary>
/// <typeparam name="TRegister">Type that each implementation implements.</typeparam>
/// <param name="implementationTypes">Types that implement RegisterType.</param>
/// <returns>MultiRegisterOptions for the fluent API.</returns>
public MultiRegisterOptions RegisterMultiple<TRegister>(IEnumerable<Type> implementationTypes) => this.RegisterMultiple(typeof(TRegister), implementationTypes);
/// <summary>
/// Register multiple implementations of a type.
///
/// Internally this registers each implementation using the full name of the class as its registration name.
/// </summary>
/// <param name="registrationType">Type that each implementation implements.</param>
/// <param name="implementationTypes">Types that implement RegisterType.</param>
/// <returns>MultiRegisterOptions for the fluent API.</returns>
public MultiRegisterOptions RegisterMultiple(Type registrationType, IEnumerable<Type> implementationTypes) {
if(implementationTypes == null) {
throw new ArgumentNullException(nameof(implementationTypes), "types is null.");
}
foreach(Type type in implementationTypes.Where(type => !registrationType.IsAssignableFrom(type))) {
throw new ArgumentException($"types: The type {registrationType.FullName} is not assignable from {type.FullName}");
}
if(implementationTypes.Count() != implementationTypes.Distinct().Count()) {
IEnumerable<String> queryForDuplicatedTypes = implementationTypes.GroupBy(i => i).Where(j => j.Count() > 1).Select(j => j.Key.FullName);
String fullNamesOfDuplicatedTypes = String.Join(",\n", queryForDuplicatedTypes.ToArray());
throw new ArgumentException($"types: The same implementation type cannot be specified multiple times for {registrationType.FullName}\n\n{fullNamesOfDuplicatedTypes}");
}
List<RegisterOptions> registerOptions = implementationTypes.Select(type => this.Register(registrationType, type, type.FullName)).ToList();
return new MultiRegisterOptions(registerOptions);
}
#endregion
#region Unregistration
/// <summary>
/// Remove a named container class registration.
/// </summary>
/// <typeparam name="TRegister">Type to unregister.</typeparam>
/// <param name="name">Name of registration.</param>
/// <returns><c>true</c> if the registration is successfully found and removed; otherwise, <c>false</c>.</returns>
public Boolean Unregister<TRegister>(String name = "") => this.Unregister(typeof(TRegister), name);
/// <summary>
/// Remove a named container class registration.
/// </summary>
/// <param name="registerType">Type to unregister.</param>
/// <param name="name">Name of registration.</param>
/// <returns><c>true</c> if the registration is successfully found and removed; otherwise, <c>false</c>.</returns>
public Boolean Unregister(Type registerType, String name = "") => this.RegisteredTypes.RemoveRegistration(new TypeRegistration(registerType, name));
#endregion
#region Resolution
/// <summary>
/// Attempts to resolve a named type using specified options and the supplied constructor parameters.
///
/// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists).
/// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail.
/// </summary>
/// <param name="resolveType">Type to resolve.</param>
/// <param name="name">Name of registration.</param>
/// <param name="options">Resolution options.</param>
/// <returns>Instance of type.</returns>
/// <exception cref="DependencyContainerResolutionException">Unable to resolve the type.</exception>
public Object Resolve(Type resolveType, String name = null, DependencyContainerResolveOptions options = null) => this.RegisteredTypes.ResolveInternal(new TypeRegistration(resolveType, name), options ?? DependencyContainerResolveOptions.Default);
/// <summary>
/// Attempts to resolve a named type using specified options and the supplied constructor parameters.
///
/// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists).
/// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail.
/// </summary>
/// <typeparam name="TResolveType">Type to resolve.</typeparam>
/// <param name="name">Name of registration.</param>
/// <param name="options">Resolution options.</param>
/// <returns>Instance of type.</returns>
/// <exception cref="DependencyContainerResolutionException">Unable to resolve the type.</exception>
public TResolveType Resolve<TResolveType>(String name = null, DependencyContainerResolveOptions options = null) where TResolveType : class => (TResolveType)this.Resolve(typeof(TResolveType), name, options);
/// <summary>
/// Attempts to predict whether a given type can be resolved with the supplied constructor parameters options.
/// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists).
/// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail.
/// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called.
/// </summary>
/// <param name="resolveType">Type to resolve.</param>
/// <param name="name">The name.</param>
/// <param name="options">Resolution options.</param>
/// <returns>
/// Bool indicating whether the type can be resolved.
/// </returns>
public Boolean CanResolve(Type resolveType, String name = null, DependencyContainerResolveOptions options = null) => this.RegisteredTypes.CanResolve(new TypeRegistration(resolveType, name), options);
/// <summary>
/// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters options.
///
/// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists).
/// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail.
///
/// Note: Resolution may still fail if user defined factory registrations fail to construct objects when called.
/// </summary>
/// <typeparam name="TResolveType">Type to resolve.</typeparam>
/// <param name="name">Name of registration.</param>
/// <param name="options">Resolution options.</param>
/// <returns>Bool indicating whether the type can be resolved.</returns>
public Boolean CanResolve<TResolveType>(String name = null, DependencyContainerResolveOptions options = null) where TResolveType : class => this.CanResolve(typeof(TResolveType), name, options);
/// <summary>
/// Attempts to resolve a type using the default options.
/// </summary>
/// <param name="resolveType">Type to resolve.</param>
/// <param name="resolvedType">Resolved type or default if resolve fails.</param>
/// <returns><c>true</c> if resolved successfully, <c>false</c> otherwise.</returns>
public Boolean TryResolve(Type resolveType, out Object resolvedType) {
try {
resolvedType = this.Resolve(resolveType);
return true;
} catch(DependencyContainerResolutionException) {
resolvedType = null;
return false;
}
}
/// <summary>
/// Attempts to resolve a type using the given options.
/// </summary>
/// <param name="resolveType">Type to resolve.</param>
/// <param name="options">Resolution options.</param>
/// <param name="resolvedType">Resolved type or default if resolve fails.</param>
/// <returns><c>true</c> if resolved successfully, <c>false</c> otherwise.</returns>
public Boolean TryResolve(Type resolveType, DependencyContainerResolveOptions options, out Object resolvedType) {
try {
resolvedType = this.Resolve(resolveType, options: options);
return true;
} catch(DependencyContainerResolutionException) {
resolvedType = null;
return false;
}
}
/// <summary>
/// Attempts to resolve a type using the default options and given name.
/// </summary>
/// <param name="resolveType">Type to resolve.</param>
/// <param name="name">Name of registration.</param>
/// <param name="resolvedType">Resolved type or default if resolve fails.</param>
/// <returns><c>true</c> if resolved successfully, <c>false</c> otherwise.</returns>
public Boolean TryResolve(Type resolveType, String name, out Object resolvedType) {
try {
resolvedType = this.Resolve(resolveType, name);
return true;
} catch(DependencyContainerResolutionException) {
resolvedType = null;
return false;
}
}
/// <summary>
/// Attempts to resolve a type using the given options and name.
/// </summary>
/// <param name="resolveType">Type to resolve.</param>
/// <param name="name">Name of registration.</param>
/// <param name="options">Resolution options.</param>
/// <param name="resolvedType">Resolved type or default if resolve fails.</param>
/// <returns><c>true</c> if resolved successfully, <c>false</c> otherwise.</returns>
public Boolean TryResolve(Type resolveType, String name, DependencyContainerResolveOptions options, out Object resolvedType) {
try {
resolvedType = this.Resolve(resolveType, name, options);
return true;
} catch(DependencyContainerResolutionException) {
resolvedType = null;
return false;
}
}
/// <summary>
/// Attempts to resolve a type using the default options.
/// </summary>
/// <typeparam name="TResolveType">Type to resolve.</typeparam>
/// <param name="resolvedType">Resolved type or default if resolve fails.</param>
/// <returns><c>true</c> if resolved successfully, <c>false</c> otherwise.</returns>
public Boolean TryResolve<TResolveType>(out TResolveType resolvedType) where TResolveType : class {
try {
resolvedType = this.Resolve<TResolveType>();
return true;
} catch(DependencyContainerResolutionException) {
resolvedType = default;
return false;
}
}
/// <summary>
/// Attempts to resolve a type using the given options.
/// </summary>
/// <typeparam name="TResolveType">Type to resolve.</typeparam>
/// <param name="options">Resolution options.</param>
/// <param name="resolvedType">Resolved type or default if resolve fails.</param>
/// <returns><c>true</c> if resolved successfully, <c>false</c> otherwise.</returns>
public Boolean TryResolve<TResolveType>(DependencyContainerResolveOptions options, out TResolveType resolvedType) where TResolveType : class {
try {
resolvedType = this.Resolve<TResolveType>(options: options);
return true;
} catch(DependencyContainerResolutionException) {
resolvedType = default;
return false;
}
}
/// <summary>
/// Attempts to resolve a type using the default options and given name.
/// </summary>
/// <typeparam name="TResolveType">Type to resolve.</typeparam>
/// <param name="name">Name of registration.</param>
/// <param name="resolvedType">Resolved type or default if resolve fails.</param>
/// <returns><c>true</c> if resolved successfully, <c>false</c> otherwise.</returns>
public Boolean TryResolve<TResolveType>(String name, out TResolveType resolvedType) where TResolveType : class {
try {
resolvedType = this.Resolve<TResolveType>(name);
return true;
} catch(DependencyContainerResolutionException) {
resolvedType = default;
return false;
}
}
/// <summary>
/// Attempts to resolve a type using the given options and name.
/// </summary>
/// <typeparam name="TResolveType">Type to resolve.</typeparam>
/// <param name="name">Name of registration.</param>
/// <param name="options">Resolution options.</param>
/// <param name="resolvedType">Resolved type or default if resolve fails.</param>
/// <returns><c>true</c> if resolved successfully, <c>false</c> otherwise.</returns>
public Boolean TryResolve<TResolveType>(String name, DependencyContainerResolveOptions options, out TResolveType resolvedType) where TResolveType : class {
try {
resolvedType = this.Resolve<TResolveType>(name, options);
return true;
} catch(DependencyContainerResolutionException) {
resolvedType = default;
return false;
}
}
/// <summary>
/// Returns all registrations of a type.
/// </summary>
/// <param name="resolveType">Type to resolveAll.</param>
/// <param name="includeUnnamed">Whether to include un-named (default) registrations.</param>
/// <returns>IEnumerable.</returns>
public IEnumerable<Object> ResolveAll(Type resolveType, Boolean includeUnnamed = false) => this.RegisteredTypes.Resolve(resolveType, includeUnnamed);
/// <summary>
/// Returns all registrations of a type.
/// </summary>
/// <typeparam name="TResolveType">Type to resolveAll.</typeparam>
/// <param name="includeUnnamed">Whether to include un-named (default) registrations.</param>
/// <returns>IEnumerable.</returns>
public IEnumerable<TResolveType> ResolveAll<TResolveType>(Boolean includeUnnamed = true) where TResolveType : class => this.ResolveAll(typeof(TResolveType), includeUnnamed).Cast<TResolveType>();
/// <summary>
/// Attempts to resolve all public property dependencies on the given object using the given resolve options.
/// </summary>
/// <param name="input">Object to "build up".</param>
/// <param name="resolveOptions">Resolve options to use.</param>
public void BuildUp(Object input, DependencyContainerResolveOptions resolveOptions = null) {
if(resolveOptions == null) {
resolveOptions = DependencyContainerResolveOptions.Default;
}
IEnumerable<PropertyInfo> properties = input.GetType().GetProperties().Where(property => property.GetCacheGetMethod() != null && property.GetCacheSetMethod() != null && !property.PropertyType.IsValueType);
foreach(PropertyInfo property in properties.Where(property => property.GetValue(input, null) == null)) {
try {
property.SetValue(input, this.RegisteredTypes.ResolveInternal(new TypeRegistration(property.PropertyType), resolveOptions), null);
} catch(DependencyContainerResolutionException) {
// Catch any resolution errors and ignore them
}
}
}
#endregion
#region Internal Methods
internal static Boolean IsValidAssignment(Type registerType, Type registerImplementation) {
if(!registerType.IsGenericTypeDefinition) {
if(!registerType.IsAssignableFrom(registerImplementation)) {
return false;
}
} else {
if(registerType.IsInterface && registerImplementation.GetInterfaces().All(t => t.Name != registerType.Name)) {
return false;
}
if(registerType.IsAbstract && registerImplementation.BaseType != registerType) {
return false;
}
}
return true;
}
private static Boolean IsIgnoredAssembly(Assembly assembly) {
// TODO - find a better way to remove "system" assemblies from the auto registration
List<Func<Assembly, Boolean>> ignoreChecks = new List<Func<Assembly, Boolean>>
{
asm => asm.FullName.StartsWith("Microsoft.", StringComparison.Ordinal),
asm => asm.FullName.StartsWith("System.", StringComparison.Ordinal),
asm => asm.FullName.StartsWith("System,", StringComparison.Ordinal),
asm => asm.FullName.StartsWith("CR_ExtUnitTest", StringComparison.Ordinal),
asm => asm.FullName.StartsWith("mscorlib,", StringComparison.Ordinal),
asm => asm.FullName.StartsWith("CR_VSTest", StringComparison.Ordinal),
asm => asm.FullName.StartsWith("DevExpress.CodeRush", StringComparison.Ordinal),
asm => asm.FullName.StartsWith("xunit.", StringComparison.Ordinal),
};
return ignoreChecks.Any(check => check(assembly));
}
private static Boolean IsIgnoredType(Type type, Func<Type, Boolean> registrationPredicate) {
// TODO - find a better way to remove "system" types from the auto registration
List<Func<Type, Boolean>> ignoreChecks = new List<Func<Type, Boolean>>()
{
t => t.FullName?.StartsWith("System.", StringComparison.Ordinal) ?? false,
t => t.FullName?.StartsWith("Microsoft.", StringComparison.Ordinal) ?? false,
t => t.IsPrimitive,
t => t.IsGenericTypeDefinition,
t => t.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length == 0 &&
!(t.IsInterface || t.IsAbstract),
};
if(registrationPredicate != null) {
ignoreChecks.Add(t => !registrationPredicate(t));
}
return ignoreChecks.Any(check => check(type));
}
private static ObjectFactoryBase GetDefaultObjectFactory(Type registerType, Type registerImplementation) => registerType.IsInterface || registerType.IsAbstract ? (ObjectFactoryBase)new SingletonFactory(registerType, registerImplementation) : new MultiInstanceFactory(registerType, registerImplementation);
#endregion
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Swan.DependencyInjection {
/// <summary>
/// Generic Constraint Registration Exception.
/// </summary>
/// <seealso cref="Exception" />
public class DependencyContainerRegistrationException : Exception {
private const String ConvertErrorText = "Cannot convert current registration of {0} to {1}";
private const String RegisterErrorText = "Cannot register type {0} - abstract classes or interfaces are not valid implementation types for {1}.";
private const String ErrorText = "Duplicate implementation of type {0} found ({1}).";
/// <summary>
/// Initializes a new instance of the <see cref="DependencyContainerRegistrationException"/> class.
/// </summary>
/// <param name="registerType">Type of the register.</param>
/// <param name="types">The types.</param>
public DependencyContainerRegistrationException(Type registerType, IEnumerable<Type> types) : base(String.Format(ErrorText, registerType, GetTypesString(types))) {
}
/// <summary>
/// Initializes a new instance of the <see cref="DependencyContainerRegistrationException" /> class.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="method">The method.</param>
/// <param name="isTypeFactory">if set to <c>true</c> [is type factory].</param>
public DependencyContainerRegistrationException(Type type, String method, Boolean isTypeFactory = false) : base(isTypeFactory ? String.Format(RegisterErrorText, type.FullName, method) : String.Format(ConvertErrorText, type.FullName, method)) {
}
private static String GetTypesString(IEnumerable<Type> types) => String.Join(",", types.Select(type => type.FullName));
}
}
@@ -0,0 +1,25 @@
using System;
namespace Swan.DependencyInjection {
/// <summary>
/// An exception for dependency resolutions.
/// </summary>
/// <seealso cref="System.Exception" />
[Serializable]
public class DependencyContainerResolutionException : Exception {
/// <summary>
/// Initializes a new instance of the <see cref="DependencyContainerResolutionException"/> class.
/// </summary>
/// <param name="type">The type.</param>
public DependencyContainerResolutionException(Type type) : base($"Unable to resolve type: {type.FullName}") {
}
/// <summary>
/// Initializes a new instance of the <see cref="DependencyContainerResolutionException"/> class.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="innerException">The inner exception.</param>
public DependencyContainerResolutionException(Type type, Exception innerException) : base($"Unable to resolve type: {type.FullName}", innerException) {
}
}
}
@@ -0,0 +1,106 @@
using System.Collections.Generic;
namespace Swan.DependencyInjection {
/// <summary>
/// Resolution settings.
/// </summary>
public class DependencyContainerResolveOptions {
/// <summary>
/// Gets the default options (attempt resolution of unregistered types, fail on named resolution if name not found).
/// </summary>
public static DependencyContainerResolveOptions Default { get; } = new DependencyContainerResolveOptions();
/// <summary>
/// Gets or sets the unregistered resolution action.
/// </summary>
/// <value>
/// The unregistered resolution action.
/// </value>
public DependencyContainerUnregisteredResolutionAction UnregisteredResolutionAction { get; set; } = DependencyContainerUnregisteredResolutionAction.AttemptResolve;
/// <summary>
/// Gets or sets the named resolution failure action.
/// </summary>
/// <value>
/// The named resolution failure action.
/// </value>
public DependencyContainerNamedResolutionFailureAction NamedResolutionFailureAction { get; set; } = DependencyContainerNamedResolutionFailureAction.Fail;
/// <summary>
/// Gets the constructor parameters.
/// </summary>
/// <value>
/// The constructor parameters.
/// </value>
public Dictionary<System.String, System.Object> ConstructorParameters { get; } = new Dictionary<System.String, System.Object>();
/// <summary>
/// Clones this instance.
/// </summary>
/// <returns></returns>
public DependencyContainerResolveOptions Clone() => new DependencyContainerResolveOptions {
NamedResolutionFailureAction = NamedResolutionFailureAction,
UnregisteredResolutionAction = UnregisteredResolutionAction,
};
}
/// <summary>
/// Defines Resolution actions.
/// </summary>
public enum DependencyContainerUnregisteredResolutionAction {
/// <summary>
/// Attempt to resolve type, even if the type isn't registered.
///
/// Registered types/options will always take precedence.
/// </summary>
AttemptResolve,
/// <summary>
/// Fail resolution if type not explicitly registered
/// </summary>
Fail,
/// <summary>
/// Attempt to resolve unregistered type if requested type is generic
/// and no registration exists for the specific generic parameters used.
///
/// Registered types/options will always take precedence.
/// </summary>
GenericsOnly,
}
/// <summary>
/// Enumerates failure actions.
/// </summary>
public enum DependencyContainerNamedResolutionFailureAction {
/// <summary>
/// The attempt unnamed resolution
/// </summary>
AttemptUnnamedResolution,
/// <summary>
/// The fail
/// </summary>
Fail,
}
/// <summary>
/// Enumerates duplicate definition actions.
/// </summary>
public enum DependencyContainerDuplicateImplementationAction {
/// <summary>
/// The register single
/// </summary>
RegisterSingle,
/// <summary>
/// The register multiple
/// </summary>
RegisterMultiple,
/// <summary>
/// The fail
/// </summary>
Fail,
}
}
@@ -0,0 +1,18 @@
using System;
namespace Swan.DependencyInjection {
/// <summary>
/// Weak Reference Exception.
/// </summary>
/// <seealso cref="System.Exception" />
public class DependencyContainerWeakReferenceException : Exception {
private const String ErrorText = "Unable to instantiate {0} - referenced object has been reclaimed";
/// <summary>
/// Initializes a new instance of the <see cref="DependencyContainerWeakReferenceException"/> class.
/// </summary>
/// <param name="type">The type.</param>
public DependencyContainerWeakReferenceException(Type type) : base(String.Format(ErrorText, type.FullName)) {
}
}
}
@@ -0,0 +1,352 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Swan.DependencyInjection {
/// <summary>
/// Represents an abstract class for Object Factory.
/// </summary>
public abstract class ObjectFactoryBase {
/// <summary>
/// Whether to assume this factory successfully constructs its objects
///
/// Generally set to true for delegate style factories as CanResolve cannot delve
/// into the delegates they contain.
/// </summary>
public virtual Boolean AssumeConstruction => false;
/// <summary>
/// The type the factory instantiates.
/// </summary>
public abstract Type CreatesType {
get;
}
/// <summary>
/// Constructor to use, if specified.
/// </summary>
public ConstructorInfo Constructor {
get; private set;
}
/// <summary>
/// Gets the singleton variant.
/// </summary>
/// <value>
/// The singleton variant.
/// </value>
/// <exception cref="DependencyContainerRegistrationException">singleton.</exception>
public virtual ObjectFactoryBase SingletonVariant => throw new DependencyContainerRegistrationException(this.GetType(), "singleton");
/// <summary>
/// Gets the multi instance variant.
/// </summary>
/// <value>
/// The multi instance variant.
/// </value>
/// <exception cref="DependencyContainerRegistrationException">multi-instance.</exception>
public virtual ObjectFactoryBase MultiInstanceVariant => throw new DependencyContainerRegistrationException(this.GetType(), "multi-instance");
/// <summary>
/// Gets the strong reference variant.
/// </summary>
/// <value>
/// The strong reference variant.
/// </value>
/// <exception cref="DependencyContainerRegistrationException">strong reference.</exception>
public virtual ObjectFactoryBase StrongReferenceVariant => throw new DependencyContainerRegistrationException(this.GetType(), "strong reference");
/// <summary>
/// Gets the weak reference variant.
/// </summary>
/// <value>
/// The weak reference variant.
/// </value>
/// <exception cref="DependencyContainerRegistrationException">weak reference.</exception>
public virtual ObjectFactoryBase WeakReferenceVariant => throw new DependencyContainerRegistrationException(this.GetType(), "weak reference");
/// <summary>
/// Create the type.
/// </summary>
/// <param name="requestedType">Type user requested to be resolved.</param>
/// <param name="container">Container that requested the creation.</param>
/// <param name="options">The options.</param>
/// <returns> Instance of type. </returns>
public abstract Object GetObject(Type requestedType, DependencyContainer container, DependencyContainerResolveOptions options);
/// <summary>
/// Gets the factory for child container.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="parent">The parent.</param>
/// <param name="child">The child.</param>
/// <returns></returns>
public virtual ObjectFactoryBase GetFactoryForChildContainer(Type type, DependencyContainer parent, DependencyContainer child) => this;
}
/// <inheritdoc />
/// <summary>
/// IObjectFactory that creates new instances of types for each resolution.
/// </summary>
internal class MultiInstanceFactory : ObjectFactoryBase {
private readonly Type _registerType;
private readonly Type _registerImplementation;
public MultiInstanceFactory(Type registerType, Type registerImplementation) {
if(registerImplementation.IsAbstract || registerImplementation.IsInterface) {
throw new DependencyContainerRegistrationException(registerImplementation, "MultiInstanceFactory", true);
}
if(!DependencyContainer.IsValidAssignment(registerType, registerImplementation)) {
throw new DependencyContainerRegistrationException(registerImplementation, "MultiInstanceFactory", true);
}
this._registerType = registerType;
this._registerImplementation = registerImplementation;
}
public override Type CreatesType => this._registerImplementation;
public override ObjectFactoryBase SingletonVariant =>
new SingletonFactory(this._registerType, this._registerImplementation);
public override ObjectFactoryBase MultiInstanceVariant => this;
public override Object GetObject(Type requestedType, DependencyContainer container, DependencyContainerResolveOptions options) {
try {
return container.RegisteredTypes.ConstructType(this._registerImplementation, this.Constructor, options);
} catch(DependencyContainerResolutionException ex) {
throw new DependencyContainerResolutionException(this._registerType, ex);
}
}
}
/// <inheritdoc />
/// <summary>
/// IObjectFactory that invokes a specified delegate to construct the object.
/// </summary>
internal class DelegateFactory : ObjectFactoryBase {
private readonly Type _registerType;
private readonly Func<DependencyContainer, Dictionary<String, Object>, Object> _factory;
public DelegateFactory(
Type registerType,
Func<DependencyContainer, Dictionary<String, Object>, Object> factory) {
this._factory = factory ?? throw new ArgumentNullException(nameof(factory));
this._registerType = registerType;
}
public override Boolean AssumeConstruction => true;
public override Type CreatesType => this._registerType;
public override ObjectFactoryBase WeakReferenceVariant => new WeakDelegateFactory(this._registerType, this._factory);
public override ObjectFactoryBase StrongReferenceVariant => this;
public override Object GetObject(Type requestedType, DependencyContainer container, DependencyContainerResolveOptions options) {
try {
return this._factory.Invoke(container, options.ConstructorParameters);
} catch(Exception ex) {
throw new DependencyContainerResolutionException(this._registerType, ex);
}
}
}
/// <inheritdoc />
/// <summary>
/// IObjectFactory that invokes a specified delegate to construct the object
/// Holds the delegate using a weak reference.
/// </summary>
internal class WeakDelegateFactory : ObjectFactoryBase {
private readonly Type _registerType;
private readonly WeakReference _factory;
public WeakDelegateFactory(Type registerType, Func<DependencyContainer, Dictionary<String, Object>, Object> factory) {
if(factory == null) {
throw new ArgumentNullException(nameof(factory));
}
this._factory = new WeakReference(factory);
this._registerType = registerType;
}
public override Boolean AssumeConstruction => true;
public override Type CreatesType => this._registerType;
public override ObjectFactoryBase StrongReferenceVariant {
get {
if(!(this._factory.Target is Func<DependencyContainer, Dictionary<global::System.String, global::System.Object>, global::System.Object> factory)) {
throw new DependencyContainerWeakReferenceException(this._registerType);
}
return new DelegateFactory(this._registerType, factory);
}
}
public override ObjectFactoryBase WeakReferenceVariant => this;
public override Object GetObject(Type requestedType, DependencyContainer container, DependencyContainerResolveOptions options) {
if(!(this._factory.Target is Func<DependencyContainer, Dictionary<global::System.String, global::System.Object>, global::System.Object> factory)) {
throw new DependencyContainerWeakReferenceException(this._registerType);
}
try {
return factory.Invoke(container, options.ConstructorParameters);
} catch(Exception ex) {
throw new DependencyContainerResolutionException(this._registerType, ex);
}
}
}
/// <summary>
/// Stores an particular instance to return for a type.
/// </summary>
internal class InstanceFactory : ObjectFactoryBase, IDisposable {
private readonly Type _registerType;
private readonly Type _registerImplementation;
private readonly Object _instance;
public InstanceFactory(Type registerType, Type registerImplementation, Object instance) {
if(!DependencyContainer.IsValidAssignment(registerType, registerImplementation)) {
throw new DependencyContainerRegistrationException(registerImplementation, "InstanceFactory", true);
}
this._registerType = registerType;
this._registerImplementation = registerImplementation;
this._instance = instance;
}
public override Boolean AssumeConstruction => true;
public override Type CreatesType => this._registerImplementation;
public override ObjectFactoryBase MultiInstanceVariant => new MultiInstanceFactory(this._registerType, this._registerImplementation);
public override ObjectFactoryBase WeakReferenceVariant => new WeakInstanceFactory(this._registerType, this._registerImplementation, this._instance);
public override ObjectFactoryBase StrongReferenceVariant => this;
public override Object GetObject(Type requestedType, DependencyContainer container, DependencyContainerResolveOptions options) => this._instance;
public void Dispose() {
IDisposable disposable = this._instance as IDisposable;
disposable?.Dispose();
}
}
/// <summary>
/// Stores the instance with a weak reference.
/// </summary>
internal class WeakInstanceFactory : ObjectFactoryBase, IDisposable {
private readonly Type _registerType;
private readonly Type _registerImplementation;
private readonly WeakReference _instance;
public WeakInstanceFactory(Type registerType, Type registerImplementation, Object instance) {
if(!DependencyContainer.IsValidAssignment(registerType, registerImplementation)) {
throw new DependencyContainerRegistrationException(registerImplementation, "WeakInstanceFactory", true);
}
this._registerType = registerType;
this._registerImplementation = registerImplementation;
this._instance = new WeakReference(instance);
}
public override Type CreatesType => this._registerImplementation;
public override ObjectFactoryBase MultiInstanceVariant => new MultiInstanceFactory(this._registerType, this._registerImplementation);
public override ObjectFactoryBase WeakReferenceVariant => this;
public override ObjectFactoryBase StrongReferenceVariant {
get {
Object instance = this._instance.Target;
if(instance == null) {
throw new DependencyContainerWeakReferenceException(this._registerType);
}
return new InstanceFactory(this._registerType, this._registerImplementation, instance);
}
}
public override Object GetObject(Type requestedType, DependencyContainer container, DependencyContainerResolveOptions options) {
Object instance = this._instance.Target;
if(instance == null) {
throw new DependencyContainerWeakReferenceException(this._registerType);
}
return instance;
}
public void Dispose() => (this._instance.Target as IDisposable)?.Dispose();
}
/// <summary>
/// A factory that lazy instantiates a type and always returns the same instance.
/// </summary>
internal class SingletonFactory : ObjectFactoryBase, IDisposable {
private readonly Type _registerType;
private readonly Type _registerImplementation;
private readonly Object _singletonLock = new Object();
private Object _current;
public SingletonFactory(Type registerType, Type registerImplementation) {
if(registerImplementation.IsAbstract || registerImplementation.IsInterface) {
throw new DependencyContainerRegistrationException(registerImplementation, nameof(SingletonFactory), true);
}
if(!DependencyContainer.IsValidAssignment(registerType, registerImplementation)) {
throw new DependencyContainerRegistrationException(registerImplementation, nameof(SingletonFactory), true);
}
this._registerType = registerType;
this._registerImplementation = registerImplementation;
}
public override Type CreatesType => this._registerImplementation;
public override ObjectFactoryBase SingletonVariant => this;
public override ObjectFactoryBase MultiInstanceVariant =>
new MultiInstanceFactory(this._registerType, this._registerImplementation);
public override Object GetObject(
Type requestedType,
DependencyContainer container,
DependencyContainerResolveOptions options) {
if(options.ConstructorParameters.Count != 0) {
throw new ArgumentException("Cannot specify parameters for singleton types");
}
lock(this._singletonLock) {
if(this._current == null) {
this._current = container.RegisteredTypes.ConstructType(this._registerImplementation, this.Constructor, options);
}
}
return this._current;
}
public override ObjectFactoryBase GetFactoryForChildContainer(
Type type,
DependencyContainer parent,
DependencyContainer child) {
// We make sure that the singleton is constructed before the child container takes the factory.
// Otherwise the results would vary depending on whether or not the parent container had resolved
// the type before the child container does.
_ = this.GetObject(type, parent, DependencyContainerResolveOptions.Default);
return this;
}
public void Dispose() => (this._current as IDisposable)?.Dispose();
}
}
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Swan.DependencyInjection {
/// <summary>
/// Registration options for "fluent" API.
/// </summary>
public sealed class RegisterOptions {
private readonly TypesConcurrentDictionary _registeredTypes;
private readonly DependencyContainer.TypeRegistration _registration;
/// <summary>
/// Initializes a new instance of the <see cref="RegisterOptions" /> class.
/// </summary>
/// <param name="registeredTypes">The registered types.</param>
/// <param name="registration">The registration.</param>
public RegisterOptions(TypesConcurrentDictionary registeredTypes, DependencyContainer.TypeRegistration registration) {
this._registeredTypes = registeredTypes;
this._registration = registration;
}
/// <summary>
/// Make registration a singleton (single instance) if possible.
/// </summary>
/// <returns>A registration options for fluent API.</returns>
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
public RegisterOptions AsSingleton() {
ObjectFactoryBase currentFactory = this._registeredTypes.GetCurrentFactory(this._registration);
if(currentFactory == null) {
throw new DependencyContainerRegistrationException(this._registration.Type, "singleton");
}
return this._registeredTypes.AddUpdateRegistration(this._registration, currentFactory.SingletonVariant);
}
/// <summary>
/// Make registration multi-instance if possible.
/// </summary>
/// <returns>A registration options for fluent API.</returns>
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
public RegisterOptions AsMultiInstance() {
ObjectFactoryBase currentFactory = this._registeredTypes.GetCurrentFactory(this._registration);
if(currentFactory == null) {
throw new DependencyContainerRegistrationException(this._registration.Type, "multi-instance");
}
return this._registeredTypes.AddUpdateRegistration(this._registration, currentFactory.MultiInstanceVariant);
}
/// <summary>
/// Make registration hold a weak reference if possible.
/// </summary>
/// <returns>A registration options for fluent API.</returns>
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
public RegisterOptions WithWeakReference() {
ObjectFactoryBase currentFactory = this._registeredTypes.GetCurrentFactory(this._registration);
if(currentFactory == null) {
throw new DependencyContainerRegistrationException(this._registration.Type, "weak reference");
}
return this._registeredTypes.AddUpdateRegistration(this._registration, currentFactory.WeakReferenceVariant);
}
/// <summary>
/// Make registration hold a strong reference if possible.
/// </summary>
/// <returns>A registration options for fluent API.</returns>
/// <exception cref="DependencyContainerRegistrationException">Generic constraint registration exception.</exception>
public RegisterOptions WithStrongReference() {
ObjectFactoryBase currentFactory = this._registeredTypes.GetCurrentFactory(this._registration);
if(currentFactory == null) {
throw new DependencyContainerRegistrationException(this._registration.Type, "strong reference");
}
return this._registeredTypes.AddUpdateRegistration(this._registration, currentFactory.StrongReferenceVariant);
}
}
/// <summary>
/// Registration options for "fluent" API when registering multiple implementations.
/// </summary>
public sealed class MultiRegisterOptions {
private IEnumerable<RegisterOptions> _registerOptions;
/// <summary>
/// Initializes a new instance of the <see cref="MultiRegisterOptions"/> class.
/// </summary>
/// <param name="registerOptions">The register options.</param>
public MultiRegisterOptions(IEnumerable<RegisterOptions> registerOptions) => this._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() {
this._registerOptions = this.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() {
this._registerOptions = this.ExecuteOnAllRegisterOptions(ro => ro.AsMultiInstance());
return this;
}
private IEnumerable<RegisterOptions> ExecuteOnAllRegisterOptions(
Func<RegisterOptions, RegisterOptions> action) => this._registerOptions.Select(action).ToList();
}
}
@@ -0,0 +1,61 @@
using System;
namespace Swan.DependencyInjection {
public partial class DependencyContainer {
/// <summary>
/// Represents a Type Registration within the IoC Container.
/// </summary>
public sealed class TypeRegistration {
private readonly Int32 _hashCode;
/// <summary>
/// Initializes a new instance of the <see cref="TypeRegistration"/> class.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="name">The name.</param>
public TypeRegistration(Type type, String name = null) {
this.Type = type;
this.Name = name ?? String.Empty;
this._hashCode = String.Concat(this.Type.FullName, "|", this.Name).GetHashCode();
}
/// <summary>
/// Gets the type.
/// </summary>
/// <value>
/// The type.
/// </value>
public Type Type {
get;
}
/// <summary>
/// Gets the name.
/// </summary>
/// <value>
/// The name.
/// </value>
public String Name {
get;
}
/// <summary>
/// Determines whether the specified <see cref="System.Object" />, is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override Boolean Equals(Object obj) => !(obj is TypeRegistration typeRegistration) || typeRegistration.Type != this.Type ? false : String.Compare(this.Name, typeRegistration.Name, StringComparison.Ordinal) == 0;
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override Int32 GetHashCode() => this._hashCode;
}
}
}
@@ -0,0 +1,265 @@
#nullable enable
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Concurrent;
namespace Swan.DependencyInjection {
/// <summary>
/// Represents a Concurrent Dictionary for TypeRegistration.
/// </summary>
public class TypesConcurrentDictionary : ConcurrentDictionary<DependencyContainer.TypeRegistration, ObjectFactoryBase> {
private static readonly ConcurrentDictionary<ConstructorInfo, ObjectConstructor> ObjectConstructorCache = new ConcurrentDictionary<ConstructorInfo, ObjectConstructor>();
private readonly DependencyContainer _dependencyContainer;
internal TypesConcurrentDictionary(DependencyContainer dependencyContainer) => this._dependencyContainer = dependencyContainer;
/// <summary>
/// Represents a delegate to build an object with the parameters.
/// </summary>
/// <param name="parameters">The parameters.</param>
/// <returns>The built object.</returns>
public delegate Object ObjectConstructor(params Object?[] parameters);
internal IEnumerable<Object> Resolve(Type resolveType, Boolean includeUnnamed) {
IEnumerable<DependencyContainer.TypeRegistration> registrations = this.Keys.Where(tr => tr.Type == resolveType).Concat(this.GetParentRegistrationsForType(resolveType)).Distinct();
if(!includeUnnamed) {
registrations = registrations.Where(tr => !String.IsNullOrEmpty(tr.Name));
}
return registrations.Select(registration => this.ResolveInternal(registration, DependencyContainerResolveOptions.Default));
}
internal ObjectFactoryBase GetCurrentFactory(DependencyContainer.TypeRegistration registration) {
_ = this.TryGetValue(registration, out ObjectFactoryBase? current);
return current!;
}
internal RegisterOptions Register(Type registerType, String name, ObjectFactoryBase factory) => this.AddUpdateRegistration(new DependencyContainer.TypeRegistration(registerType, name), factory);
internal RegisterOptions AddUpdateRegistration(DependencyContainer.TypeRegistration typeRegistration, ObjectFactoryBase factory) {
this[typeRegistration] = factory;
return new RegisterOptions(this, typeRegistration);
}
internal Boolean RemoveRegistration(DependencyContainer.TypeRegistration typeRegistration) => this.TryRemove(typeRegistration, out _);
internal Object ResolveInternal(DependencyContainer.TypeRegistration registration, DependencyContainerResolveOptions? options = null) {
if(options == null) {
options = DependencyContainerResolveOptions.Default;
}
// Attempt container resolution
if(this.TryGetValue(registration, out ObjectFactoryBase? factory)) {
try {
return factory.GetObject(registration.Type, this._dependencyContainer, options);
} catch(DependencyContainerResolutionException) {
throw;
} catch(Exception ex) {
throw new DependencyContainerResolutionException(registration.Type, ex);
}
}
// Attempt to get a factory from parent if we can
ObjectFactoryBase? bubbledObjectFactory = this.GetParentObjectFactory(registration);
if(bubbledObjectFactory != null) {
try {
return bubbledObjectFactory.GetObject(registration.Type, this._dependencyContainer, options);
} catch(DependencyContainerResolutionException) {
throw;
} catch(Exception ex) {
throw new DependencyContainerResolutionException(registration.Type, ex);
}
}
// Fail if requesting named resolution and settings set to fail if unresolved
if(!String.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == DependencyContainerNamedResolutionFailureAction.Fail) {
throw new DependencyContainerResolutionException(registration.Type);
}
// Attempted unnamed fallback container resolution if relevant and requested
if(!String.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == DependencyContainerNamedResolutionFailureAction.AttemptUnnamedResolution) {
if(this.TryGetValue(new DependencyContainer.TypeRegistration(registration.Type, String.Empty), out factory)) {
try {
return factory.GetObject(registration.Type, this._dependencyContainer, options);
} catch(DependencyContainerResolutionException) {
throw;
} catch(Exception ex) {
throw new DependencyContainerResolutionException(registration.Type, ex);
}
}
}
// Attempt unregistered construction if possible and requested
Boolean isValid = options.UnregisteredResolutionAction == DependencyContainerUnregisteredResolutionAction.AttemptResolve || registration.Type.IsGenericType && options.UnregisteredResolutionAction == DependencyContainerUnregisteredResolutionAction.GenericsOnly;
return isValid && !registration.Type.IsAbstract && !registration.Type.IsInterface ? this.ConstructType(registration.Type, null, options) : throw new DependencyContainerResolutionException(registration.Type);
}
internal Boolean CanResolve(DependencyContainer.TypeRegistration registration, DependencyContainerResolveOptions? options = null) {
if(options == null) {
options = DependencyContainerResolveOptions.Default;
}
Type checkType = registration.Type;
String name = registration.Name;
if(this.TryGetValue(new DependencyContainer.TypeRegistration(checkType, name), out ObjectFactoryBase? factory)) {
return factory.AssumeConstruction ? true : factory.Constructor == null ? this.GetBestConstructor(factory.CreatesType, options) != null : this.CanConstruct(factory.Constructor, options);
}
// Fail if requesting named resolution and settings set to fail if unresolved
// Or bubble up if we have a parent
if(!String.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == DependencyContainerNamedResolutionFailureAction.Fail) {
return this._dependencyContainer.Parent?.RegisteredTypes.CanResolve(registration, options.Clone()) ?? false;
}
// Attempted unnamed fallback container resolution if relevant and requested
if(!String.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == DependencyContainerNamedResolutionFailureAction.AttemptUnnamedResolution) {
if(this.TryGetValue(new DependencyContainer.TypeRegistration(checkType), out factory)) {
return factory.AssumeConstruction ? true : this.GetBestConstructor(factory.CreatesType, options) != null;
}
}
// Check if type is an automatic lazy factory request or an IEnumerable<ResolveType>
if(IsAutomaticLazyFactoryRequest(checkType) || registration.Type.IsIEnumerable()) {
return true;
}
// Attempt unregistered construction if possible and requested
// If we cant', bubble if we have a parent
if(options.UnregisteredResolutionAction == DependencyContainerUnregisteredResolutionAction.AttemptResolve || checkType.IsGenericType && options.UnregisteredResolutionAction == DependencyContainerUnregisteredResolutionAction.GenericsOnly) {
return this.GetBestConstructor(checkType, options) != null || (this._dependencyContainer.Parent?.RegisteredTypes.CanResolve(registration, options.Clone()) ?? false);
}
// Bubble resolution up the container tree if we have a parent
return this._dependencyContainer.Parent != null && this._dependencyContainer.Parent.RegisteredTypes.CanResolve(registration, options.Clone());
}
internal Object ConstructType(Type implementationType, ConstructorInfo? constructor, DependencyContainerResolveOptions? options = null) {
Type typeToConstruct = implementationType;
if(constructor == null) {
// Try and get the best constructor that we can construct
// if we can't construct any then get the constructor
// with the least number of parameters so we can throw a meaningful
// resolve exception
constructor = this.GetBestConstructor(typeToConstruct, options) ?? GetTypeConstructors(typeToConstruct).LastOrDefault();
}
if(constructor == null) {
throw new DependencyContainerResolutionException(typeToConstruct);
}
ParameterInfo[] ctorParams = constructor.GetParameters();
Object?[] args = new Object?[ctorParams.Length];
for(Int32 parameterIndex = 0; parameterIndex < ctorParams.Length; parameterIndex++) {
ParameterInfo currentParam = ctorParams[parameterIndex];
try {
args[parameterIndex] = options?.ConstructorParameters.GetValueOrDefault(currentParam.Name, this.ResolveInternal(new DependencyContainer.TypeRegistration(currentParam.ParameterType), options.Clone()));
} catch(DependencyContainerResolutionException ex) {
// If a constructor parameter can't be resolved
// it will throw, so wrap it and throw that this can't
// be resolved.
throw new DependencyContainerResolutionException(typeToConstruct, ex);
} catch(Exception ex) {
throw new DependencyContainerResolutionException(typeToConstruct, ex);
}
}
try {
return CreateObjectConstructionDelegateWithCache(constructor).Invoke(args);
} catch(Exception ex) {
throw new DependencyContainerResolutionException(typeToConstruct, ex);
}
}
private static ObjectConstructor CreateObjectConstructionDelegateWithCache(ConstructorInfo constructor) {
if(ObjectConstructorCache.TryGetValue(constructor, out ObjectConstructor? objectConstructor)) {
return objectConstructor;
}
// We could lock the cache here, but there's no real side
// effect to two threads creating the same ObjectConstructor
// at the same time, compared to the cost of a lock for
// every creation.
ParameterInfo[] constructorParams = constructor.GetParameters();
ParameterExpression lambdaParams = Expression.Parameter(typeof(Object[]), "parameters");
Expression[] newParams = new Expression[constructorParams.Length];
for(Int32 i = 0; i < constructorParams.Length; i++) {
BinaryExpression paramsParameter = Expression.ArrayIndex(lambdaParams, Expression.Constant(i));
newParams[i] = Expression.Convert(paramsParameter, constructorParams[i].ParameterType);
}
NewExpression newExpression = Expression.New(constructor, newParams);
LambdaExpression constructionLambda = Expression.Lambda(typeof(ObjectConstructor), newExpression, lambdaParams);
objectConstructor = (ObjectConstructor)constructionLambda.Compile();
ObjectConstructorCache[constructor] = objectConstructor;
return objectConstructor;
}
private static IEnumerable<ConstructorInfo> GetTypeConstructors(Type type) => type.GetConstructors().OrderByDescending(ctor => ctor.GetParameters().Length);
private static Boolean IsAutomaticLazyFactoryRequest(Type type) {
if(!type.IsGenericType) {
return false;
}
Type genericType = type.GetGenericTypeDefinition();
// Just a func
if(genericType == typeof(Func<>)) {
return true;
}
// 2 parameter func with string as first parameter (name)
if(genericType == typeof(Func<,>) && type.GetGenericArguments()[0] == typeof(String)) {
return true;
}
// 3 parameter func with string as first parameter (name) and IDictionary<string, object> as second (parameters)
return genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(String) && type.GetGenericArguments()[1] == typeof(IDictionary<String, Object>);
}
private ObjectFactoryBase? GetParentObjectFactory(DependencyContainer.TypeRegistration registration) => this._dependencyContainer.Parent == null
? null
: this._dependencyContainer.Parent.RegisteredTypes.TryGetValue(registration, out ObjectFactoryBase? factory) ? factory.GetFactoryForChildContainer(registration.Type, this._dependencyContainer.Parent, this._dependencyContainer) : this._dependencyContainer.Parent.RegisteredTypes.GetParentObjectFactory(registration);
private ConstructorInfo? GetBestConstructor(Type type, DependencyContainerResolveOptions? options) => type.IsValueType ? null : GetTypeConstructors(type).FirstOrDefault(ctor => this.CanConstruct(ctor, options));
private Boolean CanConstruct(MethodBase ctor, DependencyContainerResolveOptions? options) {
foreach(ParameterInfo parameter in ctor.GetParameters()) {
if(String.IsNullOrEmpty(parameter.Name)) {
return false;
}
Boolean isParameterOverload = options!.ConstructorParameters.ContainsKey(parameter.Name);
if(parameter.ParameterType.IsPrimitive && !isParameterOverload) {
return false;
}
if(!isParameterOverload && !this.CanResolve(new DependencyContainer.TypeRegistration(parameter.ParameterType), options.Clone())) {
return false;
}
}
return true;
}
private IEnumerable<DependencyContainer.TypeRegistration> GetParentRegistrationsForType(Type resolveType) => this._dependencyContainer.Parent == null ? Array.Empty<DependencyContainer.TypeRegistration>() : this._dependencyContainer.Parent.RegisteredTypes.Keys.Where(tr => tr.Type == resolveType).Concat(this._dependencyContainer.Parent.RegisteredTypes.GetParentRegistrationsForType(resolveType));
}
}