IEnumerable <TypeVault> FindRegistrations(Type type, RequestCache cache) { if (cache != null) { var cvault = cache.GetVault(type); if (cvault != null) { yield return(cvault); } } EnsureAlive(); List <TypeVault> list; if (!reverseInheritance.TryGetValue(type, out list)) { yield break; } foreach (var vault in list) { yield return(vault); } }
/// <summary> /// Resolve all registered service that inherit from, or implement or are <paramref name="type"/>. /// If none are registered, it will create and return newly create instance of <paramref name="type"/> /// , that will be saved in the <paramref name="cache"/>. /// </summary> /// <param name="type">The requested type</param> /// <param name="cache">Where newly create instance that are not service are cached for reuse.</param> /// <returns>Al registered service which implement or are subclass of <paramref name="type"/> or a newly create one.</returns> public IEnumerable <object> ResolveAll(Type type, RequestCache cache = null) { if (cache == null) { cache = new RequestCache(); } var none = true; foreach (var vault in FindRegistrations(type, cache)) { none = false; Resolve(vault, cache); yield return(vault.Instance); } if (none) { if (!CanCreate(type, cache)) { throw new InvalidOperationException($"Can't create {type.FullName}"); } var instance = Create(type, cache); yield return(instance); } }
/// <summary> /// Create that object from scratch regardless of registration /// </summary> /// <param name="type">The type to instantiate.</param> /// <param name="cache">A cache of possible instance to use as property, constructor argument and the like, on top of registered services.</param> /// <returns>A newly created instance</returns> /// <exception cref="InvalidOperationException">If no appropriate constructor can be found.</exception> public object Create(Type type, RequestCache cache = null) { bool first = createSession == null; if (first) { createSession = new List <IRegistryDelegate>(); } if (cache == null) { cache = new RequestCache(); } try { var ctors = FindConstructors(type, cache); var qc = ( from c in ctors where c.IsPublic // private constructor are private for a reason, don't use them! let ppp = c.GetParameters() orderby c.IsPublic descending, ppp.Length descending select new { c, ppp } ) .FirstOrDefault(); if (qc == null) { throw new InvalidOperationException($"Type {type.FullName} can't be Activated, no constructor can be called at this time."); } var cargs = new object[qc.ppp.Length]; for (int i = 0; i < qc.ppp.Length; i++) { var p = qc.ppp[i]; var impa = p.GetCustomAttribute <ImportAttribute>(); var t = (impa != null ? impa.ImportedType : null) ?? p.ParameterType; var vault = FindRegistrations(t, cache).FirstOrDefault(x => x.Instance != null || x.Type != type); if (vault != null) { Resolve(vault, cache); cargs[i] = vault.Instance; } else if (p.HasDefaultValue) { cargs[i] = p.DefaultValue; } else { var instance = Create(p.ParameterType, cache); cargs[i] = instance; } } var fc = FastMethod.GetMethod(qc.c); var result = fc.Invoke(null, cargs); cache[type] = result; if (result is IRegistryDelegate) { createSession.Add((IRegistryDelegate)result); } ResolveProperties(result, cache); return(result); } finally { if (first) { ForEach(createSession, x => x.OnRegistryCreated()); createSession = null; } } }
/// <summary> /// Create that object from scratch regardless of registration /// </summary> /// <typeparam name="T">The type to instantiate.</typeparam> /// <param name="cache">A cache of possible instance to use as property, constructor argument and the like, on top of registered services.</param> /// <returns>A newly created instance</returns> /// <exception cref="InvalidOperationException">If no appropriate constructor can be found.</exception> public T Create <T>(RequestCache cache = null) { return((T)Create(typeof(T), cache)); }
/// <summary> /// Whether given type can be created. Basically it must be a concrete class with some constructors /// with arguments that can also be created (or resolved). /// </summary> /// <param name="type">The type to check for creation.</param> /// <param name="cache">A cache of instance to possibly use for construction.</param> /// <returns>Whether the type can be instantiated</returns> public bool CanCreate(Type type, RequestCache cache = null) { return(FindConstructors(type, cache).Any()); }
/// <summary> /// Whether given type can be created. Basically it must be a concrete class with some constructors /// with arguments that can also be created (or resolved). /// </summary> /// <typeparam name="T">The type to check for creation.</typeparam> /// <param name="cache">A cache of instance to possibly use for construction.</param> /// <returns>Whether the type can be instantiated</returns> public bool CanCreate <T>(RequestCache cache = null) { return(CanCreate(typeof(T), cache)); }
/// <summary> /// Will set all properties marked with <see cref="ImportAttribute"/>. If property is an array or a <see cref="List{T}"/> /// It will <see cref="ResolveAll(Type, RequestCache)"/> and set up the array / collection with the result. /// </summary> /// <param name="instance">The object which property must be resolved.</param> /// <param name="cache">A cache for the request so that each instance which are not service is created only once.</param> public void ResolveProperties(object instance, RequestCache cache = null) { if (instance == null) { throw new ArgumentNullException(nameof(instance)); } if (cache == null) { cache = new RequestCache(); } var ftype = FastType.GetType(instance.GetType()); var props = ( from p in ftype.GetRuntimeMembers() where !p.IsStatic let pi = p.Member.GetCustomAttributes <ImportAttribute>().FirstOrDefault() where pi != null select new { p, pi } ).ToList(); foreach (var item in props) { var p = item.p; var pi = item.pi; if (IsBaseClass(typeof(IEnumerable), p.Type.Type)) { var t = pi.ImportedType; // T[] if (p.Type.Type.IsArray) { if (t == null) { t = p.Type.Type.GetElementType(); } if (!IsBaseClass(p.Type.Type.GetElementType(), t)) { throw new NotSupportedException($"Property {instance.GetType().Name}.{p.Name}, can't import {t.Name}"); } var objs = FindRegistrations(t, cache).Select(x => Resolve(x, cache)).ToList(); var prop = Array.CreateInstance(t, objs.Count); for (int i = 0; i < objs.Count; i++) { prop.SetValue(objs[i], i); } item.p.SetValue(instance, prop); } // List<T> else { if (!IsBaseClass(typeof(IList), p.Type.Type)) { throw new InvalidOperationException($"[Import] property {instance.GetType().Name}.{p.Name} must be an array or an IList"); } if (t == null) { var ga = p.Type.Type.GenericTypeArguments; if (ga.Length != 1) { throw new NotSupportedException($"[Import] property {instance.GetType().Name}.{p.Name} must be generic or the Import type must be defined"); } t = ga[0]; } var value = p.GetValue(instance); if (value == null) { if (!CanBeInstantiated(p.Type.Type) || !p.CanSet) { throw new InvalidOperationException($"Can't [Import]{p.Type.Type.Name} for {instance.GetType().Name}.{p.Name}"); } value = Create(p.Type.Type, cache); p.SetValue(instance, value); } var list = (IList)value; ForEach(FindRegistrations(t, cache).Select(x => Resolve(x, cache)), x => list.Add(x)); } } else { // simple property var o = ResolveAll(pi.ImportedType ?? p.Type.Type, cache).First(); item.p.SetValue(instance, o); } } }
/// <summary> /// Resolve all registered service that inherit from, or implement or are <typeparamref name="T"/>. /// If none are registered, it will create and return newly create instance of <typeparamref name="T"/> /// , that will be saved in the <paramref name="cache"/>. /// </summary> /// <typeparam name="T">The requested type</typeparam> /// <param name="cache">Where newly create instance that are not service are cached for reuse.</param> /// <returns>Al registered service which implement or are subclass of <typeparamref name="T"/> or a newly create one.</returns> public IEnumerable <T> ResolveAll <T>(RequestCache cache = null) { return(ResolveAll(typeof(T), cache).Cast <T>()); }
/// <summary> /// This will resolve the first <paramref name="type"/> with <see cref="ResolveAll(Type, RequestCache)"/> /// </summary> /// <param name="type">The requested type</param> /// <param name="cache">A cache for all instance that need be create and are not a registered service. Can be null</param> /// <returns>An instance of type <paramref name="type"/>, either a reused service if registered, or newly created instance otherwise.</returns> public object Resolve(Type type, RequestCache cache = null) { return(ResolveAll(type, cache).First()); }
/// <summary> /// This will resolve the first <typeparamref name="T"/> with <see cref="ResolveAll(Type, RequestCache)"/> /// </summary> /// <typeparam name="T">The requested type</typeparam> /// <param name="cache">A cache for all instance that need be create and are not a registered service. Can be null</param> /// <returns>An instance of <typeparamref name="T"/>, either a reused service if registered, or newly created instance otherwise.</returns> public T Resolve <T>(RequestCache cache = null) { return((T)ResolveAll(typeof(T), (RequestCache)null).First()); }