using System;
using System.Linq;
namespace Swan.DependencyInjection {
///
/// The concrete implementation of a simple IoC container
/// based largely on TinyIoC (https://github.com/grumpydev/TinyIoC).
///
///
public partial class DependencyContainer : IDisposable {
private Boolean _disposed;
///
/// Initializes a new instance of the class.
///
public DependencyContainer() {
this.RegisteredTypes = new TypesConcurrentDictionary(this);
_ = this.Register(this);
}
///
/// 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(this._disposed) {
return;
}
this._disposed = true;
foreach(IDisposable disposable in this.RegisteredTypes.Values.Select(item => item as IDisposable)) {
disposable?.Dispose();
}
GC.SuppressFinalize(this);
}
#region Registration
///
/// 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 = "") => this.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.
/// Instance of RegisterType to register.
/// Name of registration.
/// RegisterOptions for fluent API.
// [Obsolete("NEED", false)]
public RegisterOptions Register(TRegister instance, String name = "") where TRegister : class => this.Register(typeof(TRegister), instance, 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.
// [Obsolete("NEED", false)]
public Object Resolve(Type resolveType, String name = null, DependencyContainerResolveOptions options = null) => this.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.
// [Obsolete("NEED", false)]
public TResolveType Resolve(String name = null, DependencyContainerResolveOptions options = null) where TResolveType : class => (TResolveType)this.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.
///
// [Obsolete("NEED", false)]
public Boolean CanResolve(Type resolveType, String name = null, DependencyContainerResolveOptions options = null) => this.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.
// [Obsolete("NEED", false)]
public Boolean CanResolve(String name = null, DependencyContainerResolveOptions options = null) where TResolveType : class => this.CanResolve(typeof(TResolveType), name, options);
#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;
}
#endregion
}
}