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 } }