RaspberryIO/Unosquare.Swan/Components/DependencyContainer.cs
2019-12-03 18:44:25 +01:00

675 lines
31 KiB
C#

using Unosquare.Swan.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Unosquare.Swan.Components {
/// <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);
// Only register the TinyMessenger singleton if we are the root container
if(this.Parent == null) {
_ = this.Register<IMessageHub, MessageHub>();
}
}
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
#if !NETSTANDARD1_3
/// <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(
DependencyContainerDuplicateImplementationActions duplicateAction =
DependencyContainerDuplicateImplementationActions.RegisterSingle,
Func<Type, Boolean> registrationPredicate = null) => this.AutoRegister(
Runtime.GetAssemblies().Where(a => !IsIgnoredAssembly(a)),
duplicateAction,
registrationPredicate);
#endif
/// <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,
DependencyContainerDuplicateImplementationActions duplicateAction =
DependencyContainerDuplicateImplementationActions.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 == DependencyContainerDuplicateImplementationActions.Fail) {
throw new DependencyContainerRegistrationException(type, implementations);
}
if(duplicateAction == DependencyContainerDuplicateImplementationActions.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;
}
#if !NETSTANDARD1_3
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));
}
#endif
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
}
}