mirror of https://github.com/ogoun/Zero.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1312 lines
46 KiB
1312 lines
46 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using ZeroLevel.Services.Collections;
|
|
|
|
namespace ZeroLevel.DependencyInjection
|
|
{
|
|
internal sealed class Container :
|
|
IContainer
|
|
{
|
|
#region Activator
|
|
|
|
private static object Activate(Type type, object[] args)
|
|
{
|
|
var flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public;
|
|
CultureInfo culture = null; // use InvariantCulture or other if you prefer
|
|
return Activator.CreateInstance(type, flags, null, args, culture);
|
|
}
|
|
|
|
public T CreateInstance<T>(string resolveName = "")
|
|
{
|
|
T instance = default(T);
|
|
try
|
|
{
|
|
instance = Resolve<T>(resolveName);
|
|
}
|
|
catch
|
|
{
|
|
instance = (T)Activate(typeof(T), null);
|
|
}
|
|
Compose(instance);
|
|
return instance;
|
|
}
|
|
|
|
public T CreateInstance<T>(object[] args, string resolveName = "")
|
|
{
|
|
T instance = default(T);
|
|
try
|
|
{
|
|
instance = Resolve<T>(resolveName, args);
|
|
}
|
|
catch
|
|
{
|
|
instance = (T)Activate(typeof(T), args);
|
|
}
|
|
Compose(instance);
|
|
return instance;
|
|
}
|
|
|
|
public object CreateInstance(Type type, string resolveName = "")
|
|
{
|
|
object instance = null;
|
|
try
|
|
{
|
|
instance = Resolve(type, resolveName);
|
|
}
|
|
catch
|
|
{
|
|
instance = Activate(type, null);
|
|
}
|
|
Compose(instance);
|
|
return instance;
|
|
}
|
|
|
|
public object CreateInstance(Type type, object[] args, string resolveName = "")
|
|
{
|
|
object instance = null;
|
|
try
|
|
{
|
|
instance = Resolve(type, resolveName, args);
|
|
}
|
|
catch
|
|
{
|
|
instance = Activate(type, args);
|
|
}
|
|
Compose(instance);
|
|
return instance;
|
|
}
|
|
|
|
#endregion Activator
|
|
|
|
#region Caching
|
|
|
|
private readonly ReaderWriterLockSlim _rwLock =
|
|
new ReaderWriterLockSlim();
|
|
|
|
/// <summary>
|
|
/// Map - contract - dependency resolving
|
|
/// </summary>
|
|
private readonly Dictionary<Type, List<ResolveTypeInfo>> _resolvingMap =
|
|
new Dictionary<Type, List<ResolveTypeInfo>>();
|
|
|
|
private readonly object _constructorCacheeLocker = new object();
|
|
|
|
/// <summary>
|
|
/// Types constructors cache
|
|
/// </summary>
|
|
private readonly Dictionary<Type, IEnumerable<ConstructorMetadata>> _constructorCachee =
|
|
new Dictionary<Type, IEnumerable<ConstructorMetadata>>();
|
|
|
|
#endregion Caching
|
|
|
|
#region Private
|
|
|
|
/// <summary>
|
|
/// Creating an instance of an object at the specified dependency resolution
|
|
/// </summary>
|
|
/// <param name="resolveType">Dependency resolving metadata</param>
|
|
/// <param name="args">Ctor args</param>
|
|
/// <returns>Instance</returns>
|
|
private object MakeResolving(ResolveTypeInfo resolveType, object[] args, bool compose = true)
|
|
{
|
|
Type instanceType = resolveType.ImplementationType;
|
|
if (resolveType.IsShared)
|
|
{
|
|
if (null == resolveType.SharedInstance)
|
|
{
|
|
resolveType.SharedInstance = MakeInstance(instanceType, args ?? resolveType.ConstructorParameters);
|
|
if (compose)
|
|
{
|
|
Compose(resolveType.SharedInstance);
|
|
}
|
|
}
|
|
return resolveType.SharedInstance;
|
|
}
|
|
var sessionInstance = MakeInstance(instanceType, args ?? resolveType.ConstructorParameters);
|
|
if (compose)
|
|
{
|
|
Compose(sessionInstance);
|
|
}
|
|
return sessionInstance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creating an instance of the object at the specified dependency resolution, for a generic type of contract
|
|
/// </summary>
|
|
/// <param name="resolveType">Dependency resolving metadata</param>
|
|
/// <param name="genericType">Generic contract</param>
|
|
/// <param name="args">Ctor args</param>
|
|
/// <returns>Instance</returns>
|
|
private object MakeGenericResolving(ResolveTypeInfo resolveType, Type genericType, object[] args, bool compose = true)
|
|
{
|
|
if (null == resolveType.GenericCachee)
|
|
{
|
|
resolveType.GenericCachee = new Dictionary<Type, Type>();
|
|
}
|
|
if (false == resolveType.GenericCachee.ContainsKey(genericType))
|
|
{
|
|
var genericArgumentTypes = genericType.GetGenericArguments();
|
|
var realType = resolveType.ImplementationType.MakeGenericType(genericArgumentTypes);
|
|
resolveType.GenericCachee.Add(genericType, realType);
|
|
}
|
|
Type instanceType = resolveType.GenericCachee[genericType];
|
|
if (resolveType.IsShared)
|
|
{
|
|
if (resolveType.GenericInstanceCachee == null)
|
|
{
|
|
resolveType.GenericInstanceCachee = new Dictionary<Type, object>();
|
|
}
|
|
if (false == resolveType.GenericInstanceCachee.ContainsKey(instanceType))
|
|
{
|
|
var sharedInstance = MakeInstance(instanceType, args ?? resolveType.ConstructorParameters);
|
|
if (compose)
|
|
{
|
|
Compose(sharedInstance);
|
|
}
|
|
resolveType.GenericInstanceCachee.Add(instanceType, sharedInstance);
|
|
}
|
|
return resolveType.GenericInstanceCachee[instanceType];
|
|
}
|
|
var sessionInstance = MakeInstance(instanceType, args ?? resolveType.ConstructorParameters);
|
|
if (compose)
|
|
{
|
|
Compose(sessionInstance);
|
|
}
|
|
return sessionInstance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collecting properties of the type marked with attribute dependency resolution
|
|
/// </summary>
|
|
/// <param name="type">Type</param>
|
|
/// <returns>List of properties marked with "Resolve" attribute</returns>
|
|
private static IEnumerable<PropertyInfo> CollectResolvingProperties(Type type)
|
|
{
|
|
return type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy).
|
|
Where(p => p.GetCustomAttribute<ResolveAttribute>() != null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collecting fields of type marked with an attribute of dependency resolution
|
|
/// </summary>
|
|
/// <param name="type">Type</param>
|
|
/// <returns>List of properties marked with "Resolve" attribute</returns>
|
|
private static IEnumerable<FieldInfo> CollectResolvingFields(Type type)
|
|
{
|
|
return type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy).
|
|
Where(p => p.GetCustomAttribute<ResolveAttribute>() != null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Search for dependency resolution
|
|
/// </summary>
|
|
/// <param name="type">Contract</param>
|
|
/// <param name="resolveName">Dependency name</param>
|
|
/// <param name="contractType">Redefined contract type</param>
|
|
/// <returns></returns>
|
|
private ResolveTypeInfo FindResolving(Type type, string resolveName, Type contractType)
|
|
{
|
|
HashSet<Type> contract_candidates = new HashSet<Type>();
|
|
if (contractType != null)
|
|
{
|
|
if (contractType.IsInterface)
|
|
contract_candidates.Add(contractType);
|
|
foreach (var c in GetInterfacesAndAbstracts(contractType))
|
|
contract_candidates.Add(c);
|
|
}
|
|
if (contract_candidates.Count == 0)
|
|
{
|
|
if (type.IsInterface)
|
|
contract_candidates.Add(type);
|
|
foreach (var c in GetInterfacesAndAbstracts(type))
|
|
contract_candidates.Add(c);
|
|
}
|
|
if (contract_candidates.Count > 0)
|
|
{
|
|
try
|
|
{
|
|
_rwLock.EnterReadLock();
|
|
var resolveInfo = _resolvingMap.Single(r => contract_candidates.Any(ct => ct == r.Key));
|
|
return resolveInfo.Value.First(ri => ri.ResolveKey.Equals(resolveName, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
finally
|
|
{
|
|
_rwLock.ExitReadLock();
|
|
}
|
|
}
|
|
throw new KeyNotFoundException($"Can't resolve dependency by type '{type.FullName}' and dependency name '{resolveName}'");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolving dependency on attribute "Resolve
|
|
/// </summary>
|
|
/// <param name="type">Contract</param>
|
|
/// <param name="resolveAttribute">Resolve attribute</param>
|
|
/// <returns>Instance</returns>
|
|
private object MakeInstanceBy(Type type, ResolveAttribute resolveAttribute)
|
|
{
|
|
var is_generic = false;
|
|
var maybyType = type;
|
|
if (maybyType.IsGenericTypeDefinition)
|
|
{
|
|
maybyType = maybyType.GetGenericTypeDefinition();
|
|
is_generic = true;
|
|
}
|
|
var resolveType = FindResolving(maybyType,
|
|
resolveAttribute?.ResolveName ?? string.Empty,
|
|
resolveAttribute?.ContractType);
|
|
try
|
|
{
|
|
if (is_generic)
|
|
return MakeGenericResolving(resolveType, type, null);
|
|
return MakeResolving(resolveType, null);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception($"Can't create type '{type.FullName}' instance for contract type {type.FullName}. Dependency key: '{resolveAttribute?.ResolveName}'", ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collection of interfaces and abstract classes from which the type is inherited
|
|
/// </summary>
|
|
/// <param name="sourceType">Type</param>
|
|
/// <returns>List of interfaces and abstract classes</returns>
|
|
private static IEnumerable<Type> GetInterfacesAndAbstracts(Type sourceType)
|
|
{
|
|
var interfaces = sourceType.GetInterfaces().ToList();
|
|
var parent = sourceType.BaseType;
|
|
while (parent != null && parent != typeof(object))
|
|
{
|
|
if (parent.IsAbstract && parent.IsClass)
|
|
interfaces.Add(parent);
|
|
parent = parent.BaseType;
|
|
}
|
|
return interfaces;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Getting a list of metadata by type constructors
|
|
/// </summary>
|
|
/// <param name="type">Type</param>
|
|
/// <returns>Metadata type constructors</returns>
|
|
private IEnumerable<ConstructorMetadata> GetConstructors(Type type)
|
|
{
|
|
lock (_constructorCacheeLocker)
|
|
{
|
|
if (false == _constructorCachee.ContainsKey(type))
|
|
{
|
|
var list = new List<ConstructorMetadata>();
|
|
foreach (var c in type.
|
|
GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public))
|
|
{
|
|
list.Add(new ConstructorMetadata(this, c));
|
|
}
|
|
_constructorCachee.Add(type, list);
|
|
}
|
|
}
|
|
return _constructorCachee[type];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creating an instance of an object, including with non-public constructors
|
|
/// </summary>
|
|
/// <param name="type">Type</param>
|
|
/// <param name="args">Ctor args</param>
|
|
/// <returns>Instance</returns>
|
|
private object MakeInstance(Type type, object[] args)
|
|
{
|
|
ConstructorInfo constructor = null;
|
|
object[] parameters = null;
|
|
foreach (var ctor in GetConstructors(type))
|
|
{
|
|
if (ctor.IsMatch(args, out parameters))
|
|
{
|
|
constructor = ctor.Constructor;
|
|
break;
|
|
}
|
|
}
|
|
if (null == constructor)
|
|
{
|
|
return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type);
|
|
}
|
|
else
|
|
{
|
|
return constructor.Invoke(parameters);
|
|
/*var flags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public;
|
|
CultureInfo culture = null; // use InvariantCulture or other if you prefer
|
|
return Activator.CreateInstance(type, flags, null, args, culture);*/
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dependency resolution registration
|
|
/// </summary>
|
|
/// <param name="contractType">Contract</param>
|
|
/// <param name="resolveType">Dependency resolving metadata</param>
|
|
private void Register(Type contractType, ResolveTypeInfo resolveType)
|
|
{
|
|
try
|
|
{
|
|
_rwLock.EnterUpgradeableReadLock();
|
|
if (false == _resolvingMap.ContainsKey(contractType))
|
|
{
|
|
try
|
|
{
|
|
_rwLock.EnterWriteLock();
|
|
_resolvingMap.Add(contractType, new List<ResolveTypeInfo>());
|
|
}
|
|
finally
|
|
{
|
|
_rwLock.ExitWriteLock();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (resolveType.IsDefault &&
|
|
_resolvingMap[contractType].Any(it => it.IsDefault))
|
|
{
|
|
throw new Exception($"Default resolve type already has been defined. Contract: {contractType.FullName}");
|
|
}
|
|
if (_resolvingMap[contractType].
|
|
Any(it => it.ResolveKey.Equals(resolveType.ResolveKey, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
throw new Exception($"Resolve type with the same name '{resolveType.ResolveKey}' already has been defined. Contract: { contractType.FullName}");
|
|
}
|
|
}
|
|
try
|
|
{
|
|
_rwLock.EnterWriteLock();
|
|
_resolvingMap[contractType].Add(resolveType);
|
|
}
|
|
finally
|
|
{
|
|
_rwLock.ExitWriteLock();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_rwLock.ExitUpgradeableReadLock();
|
|
}
|
|
}
|
|
|
|
#endregion Private
|
|
|
|
#region Register
|
|
|
|
public void Register<TContract, TImplementation>()
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = typeof(TImplementation),
|
|
IsDefault = true,
|
|
IsShared = false,
|
|
ResolveKey = string.Empty
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void Register<TContract, TImplementation>(bool shared)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = typeof(TImplementation),
|
|
IsDefault = true,
|
|
IsShared = shared,
|
|
ResolveKey = string.Empty
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void Register<TContract, TImplementation>(string resolveName)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = typeof(TImplementation),
|
|
IsDefault = string.IsNullOrWhiteSpace(resolveName),
|
|
IsShared = false,
|
|
ResolveKey = resolveName?.Trim()
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void Register<TContract, TImplementation>(string resolveName, bool shared)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = typeof(TImplementation),
|
|
IsDefault = string.IsNullOrWhiteSpace(resolveName),
|
|
IsShared = shared,
|
|
ResolveKey = resolveName?.Trim()
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void Register(Type contractType, Type implementationType)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementationType,
|
|
IsDefault = true,
|
|
IsShared = false,
|
|
ResolveKey = string.Empty
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
public void Register(Type contractType, Type implementationType, string resolveName)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementationType,
|
|
IsDefault = string.IsNullOrWhiteSpace(resolveName),
|
|
IsShared = false,
|
|
ResolveKey = resolveName?.Trim()
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
public void Register(Type contractType, Type implementationType, bool shared)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementationType,
|
|
IsDefault = true,
|
|
IsShared = shared,
|
|
ResolveKey = string.Empty
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
public void Register(Type contractType, Type implementationType, string resolveName, bool shared)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementationType,
|
|
IsDefault = string.IsNullOrWhiteSpace(resolveName),
|
|
IsShared = shared,
|
|
ResolveKey = resolveName?.Trim()
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
#endregion Register
|
|
|
|
#region Register with parameters
|
|
|
|
public void ParameterizedRegister<TContract, TImplementation>(object[] constructorParameters)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = typeof(TImplementation),
|
|
IsDefault = true,
|
|
IsShared = false,
|
|
ResolveKey = string.Empty,
|
|
ConstructorParameters = constructorParameters
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void ParameterizedRegister<TContract, TImplementation>(string resolveName, object[] constructorParameters)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = typeof(TImplementation),
|
|
IsDefault = string.IsNullOrWhiteSpace(resolveName),
|
|
IsShared = false,
|
|
ResolveKey = resolveName?.Trim(),
|
|
ConstructorParameters = constructorParameters
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void ParameterizedRegister<TContract, TImplementation>(bool shared, object[] constructorParameters)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = typeof(TImplementation),
|
|
IsDefault = true,
|
|
IsShared = shared,
|
|
ResolveKey = string.Empty,
|
|
ConstructorParameters = constructorParameters
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void ParameterizedRegister<TContract, TImplementation>(string resolveName, bool shared, object[] constructorParameters)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = typeof(TImplementation),
|
|
IsDefault = string.IsNullOrWhiteSpace(resolveName),
|
|
IsShared = shared,
|
|
ResolveKey = resolveName?.Trim(),
|
|
ConstructorParameters = constructorParameters
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void ParameterizedRegister(Type contractType, Type implementationType, object[] constructorParameters)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementationType,
|
|
IsDefault = true,
|
|
IsShared = false,
|
|
ResolveKey = string.Empty,
|
|
ConstructorParameters = constructorParameters
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
public void ParameterizedRegister(Type contractType, Type implementationType, string resolveName, object[] constructorParameters)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementationType,
|
|
IsDefault = string.IsNullOrWhiteSpace(resolveName),
|
|
IsShared = false,
|
|
ResolveKey = resolveName?.Trim(),
|
|
ConstructorParameters = constructorParameters
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
public void ParameterizedRegister(Type contractType, Type implementationType, bool shared, object[] constructorParameters)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementationType,
|
|
IsDefault = true,
|
|
IsShared = shared,
|
|
ResolveKey = string.Empty,
|
|
ConstructorParameters = constructorParameters
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
public void ParameterizedRegister(Type contractType, Type implementationType, string resolveName, bool shared, object[] constructorParameters)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementationType,
|
|
IsDefault = string.IsNullOrWhiteSpace(resolveName),
|
|
IsShared = shared,
|
|
ResolveKey = resolveName?.Trim(),
|
|
ConstructorParameters = constructorParameters
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
#endregion Register with parameters
|
|
|
|
#region Register instance
|
|
|
|
/// <summary>
|
|
/// Register singletone
|
|
/// </summary>
|
|
/// <typeparam name="TContract">Contract</typeparam>
|
|
/// <param name="implementation">Instance</param>
|
|
public void Register<TContract>(TContract implementation)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementation.GetType(),
|
|
IsDefault = true,
|
|
IsShared = true,
|
|
ResolveKey = string.Empty,
|
|
SharedInstance = implementation
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void Register(Type contractType, object implementation)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementation.GetType(),
|
|
IsDefault = true,
|
|
IsShared = true,
|
|
ResolveKey = string.Empty,
|
|
SharedInstance = implementation
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
public void Register<TContract>(TContract implementation, string resolveName)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementation.GetType(),
|
|
IsDefault = string.IsNullOrEmpty(resolveName),
|
|
IsShared = true,
|
|
ResolveKey = resolveName,
|
|
SharedInstance = implementation
|
|
};
|
|
Register(typeof(TContract), resolveType);
|
|
}
|
|
|
|
public void Register(Type contractType, string resolveName, object implementation)
|
|
{
|
|
var resolveType = new ResolveTypeInfo
|
|
{
|
|
ImplementationType = implementation.GetType(),
|
|
IsDefault = true,
|
|
IsShared = true,
|
|
ResolveKey = resolveName,
|
|
SharedInstance = implementation
|
|
};
|
|
Register(contractType, resolveType);
|
|
}
|
|
|
|
#endregion Register instance
|
|
|
|
#region Safe register
|
|
|
|
public bool TryRegister<TContract, TImplementation>(Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
Register<TContract, TImplementation>();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryRegister<TContract, TImplementation>(bool shared, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
Register<TContract, TImplementation>(shared);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryRegister<TContract, TImplementation>(string resolveName, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
Register<TContract, TImplementation>(resolveName);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryRegister<TContract, TImplementation>(string resolveName, bool shared, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
Register<TContract, TImplementation>(resolveName, shared);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryRegister(Type contractType, Type implementationType, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
Register(contractType, implementationType);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryRegister(Type contractType, Type implementationType, string resolveName, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
Register(contractType, implementationType, resolveName);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryRegister(Type contractType, Type implementationType, bool shared, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
Register(contractType, implementationType, shared);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryRegister(Type contractType, Type implementationType, string resolveName, bool shared, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
Register(contractType, implementationType, resolveName, shared);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endregion Safe register
|
|
|
|
#region Safe register with parameters
|
|
|
|
public bool TryParameterizedRegister<TContract, TImplementation>(object[] constructorParameters, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
ParameterizedRegister<TContract, TImplementation>(constructorParameters);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryParameterizedRegister<TContract, TImplementation>(string resolveName, object[] constructorParameters, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
ParameterizedRegister<TContract, TImplementation>(resolveName, constructorParameters);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryParameterizedRegister<TContract, TImplementation>(bool shared, object[] constructorParameters, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
ParameterizedRegister<TContract, TImplementation>(shared, constructorParameters);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryParameterizedRegister<TContract, TImplementation>(string resolveName, bool shared, object[] constructorParameters, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
ParameterizedRegister<TContract, TImplementation>(resolveName, shared, constructorParameters);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryParameterizedRegister(Type contractType, Type implementationType, object[] constructorParameters, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
ParameterizedRegister(contractType, implementationType, constructorParameters);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryParameterizedRegister(Type contractType, Type implementationType, string resolveName, object[] constructorParameters, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
ParameterizedRegister(contractType, implementationType, resolveName, constructorParameters);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryParameterizedRegister(Type contractType, Type implementationType, bool shared, object[] constructorParameters, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
ParameterizedRegister(contractType, implementationType, shared, constructorParameters);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool TryParameterizedRegister(Type contractType, Type implementationType, string resolveName, bool shared, object[] constructorParameters, Action<Exception> fallback = null)
|
|
{
|
|
try
|
|
{
|
|
ParameterizedRegister(contractType, implementationType, resolveName, shared, constructorParameters);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
fallback?.Invoke(ex);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endregion Safe register with parameters
|
|
|
|
#region Resolving
|
|
|
|
public object Resolve(Type type, bool compose = true)
|
|
{
|
|
return Resolve(type, string.Empty, null, compose);
|
|
}
|
|
|
|
public object Resolve(Type type, object[] args, bool compose = true)
|
|
{
|
|
return Resolve(type, string.Empty, args, compose);
|
|
}
|
|
|
|
public object Resolve(Type type, string resolveName, bool compose = true)
|
|
{
|
|
return Resolve(type, resolveName, null, compose);
|
|
}
|
|
|
|
public T Resolve<T>(bool compose = true)
|
|
{
|
|
return (T)Resolve(typeof(T), string.Empty, null, compose);
|
|
}
|
|
|
|
public T Resolve<T>(object[] args, bool compose = true)
|
|
{
|
|
return (T)Resolve(typeof(T), string.Empty, args, compose);
|
|
}
|
|
|
|
public T Resolve<T>(string resolveName, bool compose = true)
|
|
{
|
|
return (T)Resolve(typeof(T), resolveName, null, compose);
|
|
}
|
|
|
|
public T Resolve<T>(string resolveName, object[] args, bool compose = true)
|
|
{
|
|
return (T)Resolve(typeof(T), resolveName, args, compose);
|
|
}
|
|
|
|
public bool IsResolvingExists<T>()
|
|
{
|
|
return IsResolvingExists(typeof(T), string.Empty);
|
|
}
|
|
|
|
public bool IsResolvingExists<T>(string resolveName)
|
|
{
|
|
return IsResolvingExists(typeof(T), resolveName);
|
|
}
|
|
|
|
public bool IsResolvingExists(Type type)
|
|
{
|
|
return IsResolvingExists(type, string.Empty);
|
|
}
|
|
|
|
public bool IsResolvingExists(Type type, string resolveName)
|
|
{
|
|
return GetResolvedType(type, resolveName)?.Item1 != null;
|
|
}
|
|
|
|
private Tuple<ResolveTypeInfo, bool> GetResolvedType(Type type, string resolveName)
|
|
{
|
|
try
|
|
{
|
|
_rwLock.EnterReadLock();
|
|
if (_resolvingMap.ContainsKey(type))
|
|
{
|
|
return Tuple.Create(_resolvingMap[type].
|
|
FirstOrDefault(i => i.ResolveKey.Equals(resolveName, StringComparison.Ordinal)),
|
|
false);
|
|
}
|
|
else if (type.IsGenericType)
|
|
{
|
|
var generic_type = type.GetGenericTypeDefinition();
|
|
if (_resolvingMap.ContainsKey(generic_type))
|
|
{
|
|
return Tuple.Create(_resolvingMap[generic_type].
|
|
FirstOrDefault(i => i.ResolveKey.Equals(resolveName, StringComparison.Ordinal)),
|
|
true);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
_rwLock.ExitReadLock();
|
|
}
|
|
return new Tuple<ResolveTypeInfo, bool>(null, false);
|
|
}
|
|
|
|
public object Resolve(Type type, string resolveName, object[] args, bool compose = true)
|
|
{
|
|
var resolve = GetResolvedType(type, resolveName);
|
|
if (null == resolve.Item1)
|
|
throw new KeyNotFoundException($"Can'r resolve type {type.FullName} on key '{resolveName}'");
|
|
// Detect instance type
|
|
try
|
|
{
|
|
if (resolve.Item2)
|
|
{
|
|
return MakeGenericResolving(resolve.Item1, type, args, compose);
|
|
}
|
|
return MakeResolving(resolve.Item1, args, compose);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception($"Can't create instance for type {type.FullName} for resolved dependency {type.FullName} by key {resolveName}", ex);
|
|
}
|
|
}
|
|
|
|
#endregion Resolving
|
|
|
|
#region Safe resolving
|
|
|
|
public object TryResolve(Type type, out object result, bool compose = true)
|
|
{
|
|
return TryResolve(type, string.Empty, null, out result, compose);
|
|
}
|
|
|
|
public object TryResolve(Type type, object[] args, out object result, bool compose = true)
|
|
{
|
|
return TryResolve(type, string.Empty, args, out result, compose);
|
|
}
|
|
|
|
public object TryResolve(Type type, string resolveName, out object result, bool compose = true)
|
|
{
|
|
return TryResolve(type, resolveName, null, out result, compose);
|
|
}
|
|
|
|
public bool TryResolve<T>(out T result, bool compose = true)
|
|
{
|
|
object instance;
|
|
if (TryResolve(typeof(T), string.Empty, null, out instance, compose))
|
|
{
|
|
result = (T)instance;
|
|
return true;
|
|
}
|
|
result = default(T);
|
|
return false;
|
|
}
|
|
|
|
public bool TryResolve<T>(object[] args, out T result, bool compose = true)
|
|
{
|
|
object instance;
|
|
if (TryResolve(typeof(T), string.Empty, args, out instance, compose))
|
|
{
|
|
result = (T)instance;
|
|
return true;
|
|
}
|
|
result = default(T);
|
|
return false;
|
|
}
|
|
|
|
public bool TryResolve<T>(string resolveName, out T result, bool compose = true)
|
|
{
|
|
object instance;
|
|
if (TryResolve(typeof(T), resolveName, null, out instance, compose))
|
|
{
|
|
result = (T)instance;
|
|
return true;
|
|
}
|
|
result = default(T);
|
|
return false;
|
|
}
|
|
|
|
public bool TryResolve<T>(string resolveName, object[] args, out T result, bool compose = true)
|
|
{
|
|
object instance;
|
|
if (TryResolve(typeof(T), resolveName, args, out instance, compose))
|
|
{
|
|
result = (T)instance;
|
|
return true;
|
|
}
|
|
result = default(T);
|
|
return false;
|
|
}
|
|
|
|
public bool TryResolve(Type type, string resolveName, object[] args, out object result, bool compose = true)
|
|
{
|
|
// Detect instance type
|
|
var resolve = GetResolvedType(type, resolveName);
|
|
if (null == resolve.Item1)
|
|
{
|
|
result = null;
|
|
return false;
|
|
}
|
|
try
|
|
{
|
|
if (resolve.Item2)
|
|
{
|
|
result = MakeGenericResolving(resolve.Item1, type, args, compose);
|
|
}
|
|
else
|
|
{
|
|
result = MakeResolving(resolve.Item1, args, compose);
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.SystemWarning(
|
|
$"Can't create type '{type.FullName}' instance for resolve dependency with contract type '{type.FullName}' and dependency name '{resolveName}'", ex);
|
|
}
|
|
result = null;
|
|
return false;
|
|
}
|
|
|
|
#endregion Safe resolving
|
|
|
|
#region Composition
|
|
|
|
/// <summary>
|
|
/// Filling in the fields and properties of an object with auto-set values flagged from the container parameters
|
|
/// </summary>
|
|
private void FillParametrizedFieldsAndProperties(object instance)
|
|
{
|
|
foreach (var property in instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy))
|
|
{
|
|
var attr = property.GetCustomAttribute<ParameterAttribute>();
|
|
if (attr != null)
|
|
{
|
|
var parameterType = attr.Type ?? property.PropertyType;
|
|
var parameterName = attr.Name ?? property.Name;
|
|
property.SetValue(instance, this.Get(parameterType, parameterName));
|
|
}
|
|
}
|
|
foreach (var field in instance.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy))
|
|
{
|
|
var attr = field.GetCustomAttribute<ParameterAttribute>();
|
|
if (attr != null)
|
|
{
|
|
var parameterType = attr.Type ?? field.FieldType;
|
|
var parameterName = string.IsNullOrWhiteSpace(attr.Name) ? field.Name : attr.Name;
|
|
field.SetValue(instance, this.Get(parameterType, parameterName));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ComposeParts(object instance)
|
|
{
|
|
var resolve_properties = CollectResolvingProperties(instance.GetType());
|
|
var resolve_fields = CollectResolvingFields(instance.GetType());
|
|
foreach (var p in resolve_properties)
|
|
{
|
|
var resolve_instance = MakeInstanceBy(p.PropertyType,
|
|
p.GetCustomAttribute<ResolveAttribute>());
|
|
p.SetValue(instance, resolve_instance);
|
|
}
|
|
foreach (var f in resolve_fields)
|
|
{
|
|
var resolve_instance = MakeInstanceBy(f.FieldType,
|
|
f.GetCustomAttribute<ResolveAttribute>());
|
|
f.SetValue(instance, resolve_instance);
|
|
}
|
|
FillParametrizedFieldsAndProperties(instance);
|
|
}
|
|
|
|
private void RecursiveCompose(object instance, HashSet<object> set)
|
|
{
|
|
foreach (var f in
|
|
instance.GetType().GetFields(BindingFlags.Public |
|
|
BindingFlags.NonPublic |
|
|
BindingFlags.Instance))
|
|
{
|
|
if (f.FieldType.IsClass || f.FieldType.IsInterface)
|
|
{
|
|
var next = f.GetValue(instance);
|
|
if (null != next)
|
|
{
|
|
if (set.Add(next))
|
|
{
|
|
RecursiveCompose(next, set);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ComposeParts(instance);
|
|
}
|
|
|
|
public void Compose(object instance, bool recursive = true)
|
|
{
|
|
if (recursive)
|
|
{
|
|
RecursiveCompose(instance, new HashSet<object>());
|
|
}
|
|
else
|
|
{
|
|
ComposeParts(instance);
|
|
}
|
|
}
|
|
|
|
public bool TryCompose(object instanse, bool recursive = true)
|
|
{
|
|
try
|
|
{
|
|
Compose(instanse, recursive);
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.SystemError(ex, $"[Container] TryCompose error. Instance: '{instanse?.GetType()?.FullName ?? string.Empty}'. Recursive: {recursive}");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion Composition
|
|
|
|
#region IDisposable
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
_rwLock.EnterWriteLock();
|
|
foreach (var list in _resolvingMap.Values)
|
|
{
|
|
foreach (var item in list)
|
|
{
|
|
try
|
|
{
|
|
(item.SharedInstance as IDisposable)?.Dispose();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.SystemError(ex, $"[Container] Singletone dispose error. Instance: '{item?.GetType()?.FullName ?? string.Empty}'");
|
|
}
|
|
if (item.GenericInstanceCachee != null)
|
|
{
|
|
foreach (var gitem in item.GenericInstanceCachee.Values)
|
|
{
|
|
try
|
|
{
|
|
(gitem as IDisposable)?.Dispose();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.SystemError(ex, $"[Container] Generic singletone dispose error. Instance: '{gitem?.GetType()?.FullName ?? string.Empty}'");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.SystemError(ex, $"[Container] Dispose error");
|
|
}
|
|
finally
|
|
{
|
|
_rwLock.ExitWriteLock();
|
|
}
|
|
}
|
|
|
|
#endregion IDisposable
|
|
|
|
#region IEverythingStorage
|
|
|
|
private readonly Lazy<IEverythingStorage> _everything =
|
|
new Lazy<IEverythingStorage>(EverythingStorage.Create);
|
|
|
|
public void Save<T>(string key, T value)
|
|
{
|
|
_everything.Value.Add<T>(key, value);
|
|
}
|
|
|
|
public void Remove<T>(string key)
|
|
{
|
|
_everything.Value.Remove<T>(key);
|
|
}
|
|
|
|
public bool Contains<T>(string key)
|
|
{
|
|
return _everything.Value.ContainsKey<T>(key);
|
|
}
|
|
|
|
public bool TrySave<T>(string key, T value)
|
|
{
|
|
return _everything.Value.TryAdd<T>(key, value);
|
|
}
|
|
|
|
public bool TryRemove<T>(string key)
|
|
{
|
|
return _everything.Value.TryRemove<T>(key);
|
|
}
|
|
|
|
public T Get<T>(string key)
|
|
{
|
|
return _everything.Value.Get<T>(key);
|
|
}
|
|
|
|
public object Get(Type type, string key)
|
|
{
|
|
return _everything.Value.Get(type, key);
|
|
}
|
|
|
|
public void SaveOrUpdate<T>(string key, T value)
|
|
{
|
|
_everything.Value.AddOrUpdate<T>(key, value);
|
|
}
|
|
|
|
public T GetOrDefault<T>(string key)
|
|
{
|
|
if (_everything.Value.ContainsKey<T>(key))
|
|
return _everything.Value.Get<T>(key);
|
|
return default(T);
|
|
}
|
|
|
|
public T GetOrDefault<T>(string key, T defaultValue)
|
|
{
|
|
if (_everything.Value.ContainsKey<T>(key))
|
|
return _everything.Value.Get<T>(key);
|
|
return defaultValue;
|
|
}
|
|
|
|
#endregion IEverythingStorage
|
|
}
|
|
} |