2019-12-09 17:25:54 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
|
|
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 Boolean _disposed;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="DependencyContainer"/> class.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public DependencyContainer() {
|
|
|
|
|
this.RegisteredTypes = new TypesConcurrentDictionary(this);
|
|
|
|
|
_ = this.Register(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Registration
|
|
|
|
|
|
|
|
|
|
/// <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>
|
|
|
|
|
/// <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>
|
2019-12-10 20:01:19 +01:00
|
|
|
|
// [Obsolete("NEED", false)]
|
2019-12-09 17:25:54 +01:00
|
|
|
|
public RegisterOptions Register<TRegister>(TRegister instance, String name = "") where TRegister : class => this.Register(typeof(TRegister), instance, 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>
|
2019-12-10 20:01:19 +01:00
|
|
|
|
// [Obsolete("NEED", false)]
|
2019-12-09 17:25:54 +01:00
|
|
|
|
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>
|
2019-12-10 20:01:19 +01:00
|
|
|
|
// [Obsolete("NEED", false)]
|
2019-12-09 17:25:54 +01:00
|
|
|
|
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>
|
2019-12-10 20:01:19 +01:00
|
|
|
|
// [Obsolete("NEED", false)]
|
2019-12-09 17:25:54 +01:00
|
|
|
|
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>
|
2019-12-10 20:01:19 +01:00
|
|
|
|
// [Obsolete("NEED", false)]
|
2019-12-09 17:25:54 +01:00
|
|
|
|
public Boolean CanResolve<TResolveType>(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
|
|
|
|
|
}
|
|
|
|
|
}
|