namespace Swan.DependencyInjection
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
///
/// The concrete implementation of a simple IoC container
/// based largely on TinyIoC (https://github.com/grumpydev/TinyIoC).
///
///
public partial class DependencyContainer : IDisposable
{
private readonly object _autoRegisterLock = new object();
private bool _disposed;
static DependencyContainer()
{
}
///
/// Initializes a new instance of the class.
///
public DependencyContainer()
{
RegisteredTypes = new TypesConcurrentDictionary(this);
Register(this);
}
private DependencyContainer(DependencyContainer parent)
: this()
{
Parent = parent;
}
///
/// Lazy created Singleton instance of the container for simple scenarios.
///
public static DependencyContainer Current { get; } = new DependencyContainer();
internal DependencyContainer Parent { get; }
internal TypesConcurrentDictionary RegisteredTypes { get; }
///
public void Dispose()
{
if (_disposed) return;
_disposed = true;
foreach (var disposable in RegisteredTypes.Values.Select(item => item as IDisposable))
{
disposable?.Dispose();
}
GC.SuppressFinalize(this);
}
///
/// Gets the child container.
///
/// A new instance of the class.
public DependencyContainer GetChildContainer() => new DependencyContainer(this);
#region Registration
///
/// 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.
///
/// What action to take when encountering duplicate implementations of an interface/base class.
/// Predicate to determine if a particular type should be registered.
public void AutoRegister(
DependencyContainerDuplicateImplementationAction duplicateAction =
DependencyContainerDuplicateImplementationAction.RegisterSingle,
Func registrationPredicate = null)
{
AutoRegister(
AppDomain.CurrentDomain.GetAssemblies().Where(a => !IsIgnoredAssembly(a)),
duplicateAction,
registrationPredicate);
}
///
/// 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.
///
/// Assemblies to process.
/// What action to take when encountering duplicate implementations of an interface/base class.
/// Predicate to determine if a particular type should be registered.
public void AutoRegister(
IEnumerable assemblies,
DependencyContainerDuplicateImplementationAction duplicateAction =
DependencyContainerDuplicateImplementationAction.RegisterSingle,
Func registrationPredicate = null)
{
lock (_autoRegisterLock)
{
var types = assemblies
.SelectMany(a => a.GetAllTypes())
.Where(t => !IsIgnoredType(t, registrationPredicate))
.ToList();
var concreteTypes = types
.Where(type =>
type.IsClass && !type.IsAbstract &&
(type != GetType() && (type.DeclaringType != GetType()) && !type.IsGenericTypeDefinition))
.ToList();
foreach (var type in concreteTypes)
{
try
{
RegisteredTypes.Register(type, string.Empty, GetDefaultObjectFactory(type, type));
}
catch (MethodAccessException)
{
// Ignore methods we can't access - added for Silverlight
}
}
var abstractInterfaceTypes = types.Where(
type =>
((type.IsInterface || type.IsAbstract) && (type.DeclaringType != GetType()) &&
(!type.IsGenericTypeDefinition)));
foreach (var type in abstractInterfaceTypes)
{
var localType = type;
var 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)
{
RegisterMultiple(type, implementations);
}
}
var firstImplementation = implementations.FirstOrDefault();
if (firstImplementation == null) continue;
try
{
RegisteredTypes.Register(type, string.Empty, GetDefaultObjectFactory(type, firstImplementation));
}
catch (MethodAccessException)
{
// Ignore methods we can't access - added for Silverlight
}
}
}
}
///
/// Creates/replaces a named container class registration with default options.
///
/// Type to register.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(Type registerType, string name = "")
=> RegisteredTypes.Register(
registerType,
name,
GetDefaultObjectFactory(registerType, registerType));
///
/// Creates/replaces a named container class registration with a given implementation and default options.
///
/// Type to register.
/// Type to instantiate that implements RegisterType.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(Type registerType, Type registerImplementation, string name = "") =>
RegisteredTypes.Register(registerType, name, GetDefaultObjectFactory(registerType, registerImplementation));
///
/// Creates/replaces a named container class registration with a specific, strong referenced, instance.
///
/// Type to register.
/// Instance of RegisterType to register.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(Type registerType, object instance, string name = "") =>
RegisteredTypes.Register(registerType, name, new InstanceFactory(registerType, registerType, instance));
///
/// Creates/replaces a named container class registration with a specific, strong referenced, instance.
///
/// Type to register.
/// Type of instance to register that implements RegisterType.
/// Instance of RegisterImplementation to register.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(
Type registerType,
Type registerImplementation,
object instance,
string name = "")
=> RegisteredTypes.Register(registerType, name, new InstanceFactory(registerType, registerImplementation, instance));
///
/// Creates/replaces a container class registration with a user specified factory.
///
/// Type to register.
/// Factory/lambda that returns an instance of RegisterType.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(
Type registerType,
Func, object> factory,
string name = "")
=> RegisteredTypes.Register(registerType, name, new DelegateFactory(registerType, factory));
///
/// Creates/replaces a named container class registration with default options.
///
/// Type to register.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(string name = "")
where TRegister : class
{
return Register(typeof(TRegister), name);
}
///
/// Creates/replaces a named container class registration with a given implementation and default options.
///
/// Type to register.
/// Type to instantiate that implements RegisterType.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(string name = "")
where TRegister : class
where TRegisterImplementation : class, TRegister
{
return Register(typeof(TRegister), typeof(TRegisterImplementation), name);
}
///
/// Creates/replaces a named container class registration with a specific, strong referenced, instance.
///
/// Type to register.
/// Instance of RegisterType to register.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(TRegister instance, string name = "")
where TRegister : class
{
return Register(typeof(TRegister), instance, name);
}
///
/// Creates/replaces a named container class registration with a specific, strong referenced, instance.
///
/// Type to register.
/// Type of instance to register that implements RegisterType.
/// Instance of RegisterImplementation to register.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(TRegisterImplementation instance,
string name = "")
where TRegister : class
where TRegisterImplementation : class, TRegister
{
return Register(typeof(TRegister), typeof(TRegisterImplementation), instance, name);
}
///
/// Creates/replaces a named container class registration with a user specified factory.
///
/// Type to register.
/// Factory/lambda that returns an instance of RegisterType.
/// Name of registration.
/// RegisterOptions for fluent API.
public RegisterOptions Register(
Func, TRegister> factory, string name = "")
where TRegister : class
{
if (factory == null)
throw new ArgumentNullException(nameof(factory));
return Register(typeof(TRegister), factory, name);
}
///
/// Register multiple implementations of a type.
///
/// Internally this registers each implementation using the full name of the class as its registration name.
///
/// Type that each implementation implements.
/// Types that implement RegisterType.
/// MultiRegisterOptions for the fluent API.
public MultiRegisterOptions RegisterMultiple(IEnumerable implementationTypes) =>
RegisterMultiple(typeof(TRegister), implementationTypes);
///
/// Register multiple implementations of a type.
///
/// Internally this registers each implementation using the full name of the class as its registration name.
///
/// Type that each implementation implements.
/// Types that implement RegisterType.
/// MultiRegisterOptions for the fluent API.
public MultiRegisterOptions RegisterMultiple(Type registrationType, IEnumerable implementationTypes)
{
if (implementationTypes == null)
throw new ArgumentNullException(nameof(implementationTypes), "types is null.");
foreach (var 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())
{
var queryForDuplicatedTypes = implementationTypes
.GroupBy(i => i)
.Where(j => j.Count() > 1)
.Select(j => j.Key.FullName);
var 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}");
}
var registerOptions = implementationTypes
.Select(type => Register(registrationType, type, type.FullName))
.ToList();
return new MultiRegisterOptions(registerOptions);
}
#endregion
#region Unregistration
///
/// Remove a named container class registration.
///
/// Type to unregister.
/// Name of registration.
/// true if the registration is successfully found and removed; otherwise, false.
public bool Unregister(string name = "") => Unregister(typeof(TRegister), name);
///
/// Remove a named container class registration.
///
/// Type to unregister.
/// Name of registration.
/// true if the registration is successfully found and removed; otherwise, false.
public bool Unregister(Type registerType, string name = "") =>
RegisteredTypes.RemoveRegistration(new TypeRegistration(registerType, name));
#endregion
#region Resolution
///
/// 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.
///
/// Type to resolve.
/// Name of registration.
/// Resolution options.
/// Instance of type.
/// Unable to resolve the type.
public object Resolve(
Type resolveType,
string name = null,
DependencyContainerResolveOptions options = null)
=> RegisteredTypes.ResolveInternal(new TypeRegistration(resolveType, name), options ?? DependencyContainerResolveOptions.Default);
///
/// 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.
///
/// Type to resolve.
/// Name of registration.
/// Resolution options.
/// Instance of type.
/// Unable to resolve the type.
public TResolveType Resolve(
string name = null,
DependencyContainerResolveOptions options = null)
where TResolveType : class
{
return (TResolveType)Resolve(typeof(TResolveType), name, options);
}
///
/// 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.
///
/// Type to resolve.
/// The name.
/// Resolution options.
///
/// Bool indicating whether the type can be resolved.
///
public bool CanResolve(
Type resolveType,
string name = null,
DependencyContainerResolveOptions options = null) =>
RegisteredTypes.CanResolve(new TypeRegistration(resolveType, name), options);
///
/// 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.
///
/// Type to resolve.
/// Name of registration.
/// Resolution options.
/// Bool indicating whether the type can be resolved.
public bool CanResolve(
string name = null,
DependencyContainerResolveOptions options = null)
where TResolveType : class
{
return CanResolve(typeof(TResolveType), name, options);
}
///
/// Attempts to resolve a type using the default options.
///
/// Type to resolve.
/// Resolved type or default if resolve fails.
/// true if resolved successfully, false otherwise.
public bool TryResolve(Type resolveType, out object resolvedType)
{
try
{
resolvedType = Resolve(resolveType);
return true;
}
catch (DependencyContainerResolutionException)
{
resolvedType = null;
return false;
}
}
///
/// Attempts to resolve a type using the given options.
///
/// Type to resolve.
/// Resolution options.
/// Resolved type or default if resolve fails.
/// true if resolved successfully, false otherwise.
public bool TryResolve(Type resolveType, DependencyContainerResolveOptions options, out object resolvedType)
{
try
{
resolvedType = Resolve(resolveType, options: options);
return true;
}
catch (DependencyContainerResolutionException)
{
resolvedType = null;
return false;
}
}
///
/// Attempts to resolve a type using the default options and given name.
///
/// Type to resolve.
/// Name of registration.
/// Resolved type or default if resolve fails.
/// true if resolved successfully, false otherwise.
public bool TryResolve(Type resolveType, string name, out object resolvedType)
{
try
{
resolvedType = Resolve(resolveType, name);
return true;
}
catch (DependencyContainerResolutionException)
{
resolvedType = null;
return false;
}
}
///
/// Attempts to resolve a type using the given options and name.
///
/// Type to resolve.
/// Name of registration.
/// Resolution options.
/// Resolved type or default if resolve fails.
/// true if resolved successfully, false otherwise.
public bool TryResolve(
Type resolveType,
string name,
DependencyContainerResolveOptions options,
out object resolvedType)
{
try
{
resolvedType = Resolve(resolveType, name, options);
return true;
}
catch (DependencyContainerResolutionException)
{
resolvedType = null;
return false;
}
}
///
/// Attempts to resolve a type using the default options.
///
/// Type to resolve.
/// Resolved type or default if resolve fails.
/// true if resolved successfully, false otherwise.
public bool TryResolve(out TResolveType resolvedType)
where TResolveType : class
{
try
{
resolvedType = Resolve();
return true;
}
catch (DependencyContainerResolutionException)
{
resolvedType = default;
return false;
}
}
///
/// Attempts to resolve a type using the given options.
///
/// Type to resolve.
/// Resolution options.
/// Resolved type or default if resolve fails.
/// true if resolved successfully, false otherwise.
public bool TryResolve(DependencyContainerResolveOptions options, out TResolveType resolvedType)
where TResolveType : class
{
try
{
resolvedType = Resolve(options: options);
return true;
}
catch (DependencyContainerResolutionException)
{
resolvedType = default;
return false;
}
}
///
/// Attempts to resolve a type using the default options and given name.
///
/// Type to resolve.
/// Name of registration.
/// Resolved type or default if resolve fails.
/// true if resolved successfully, false otherwise.
public bool TryResolve(string name, out TResolveType resolvedType)
where TResolveType : class
{
try
{
resolvedType = Resolve(name);
return true;
}
catch (DependencyContainerResolutionException)
{
resolvedType = default;
return false;
}
}
///
/// Attempts to resolve a type using the given options and name.
///
/// Type to resolve.
/// Name of registration.
/// Resolution options.
/// Resolved type or default if resolve fails.
/// true if resolved successfully, false otherwise.
public bool TryResolve(
string name,
DependencyContainerResolveOptions options,
out TResolveType resolvedType)
where TResolveType : class
{
try
{
resolvedType = Resolve(name, options);
return true;
}
catch (DependencyContainerResolutionException)
{
resolvedType = default;
return false;
}
}
///
/// Returns all registrations of a type.
///
/// Type to resolveAll.
/// Whether to include un-named (default) registrations.
/// IEnumerable.
public IEnumerable