using System; using System.Linq; using System.Reflection; using Umbraco.Core.Composing; namespace Umbraco.Core { /// /// Provides extension methods to the class. /// public static class FactoryExtensions { /// /// Gets an instance of a service. /// /// The type of the service. /// The factory. /// An instance of the specified type. /// Throws an exception if the factory failed to get an instance of the specified type. public static T GetInstance(this IFactory factory) where T : class => (T)factory.GetInstance(typeof(T)); /// /// Tries to get an instance of a service. /// /// The type of the service. /// An instance of the specified type, or null. /// Returns null if the factory does not know how to get an instance /// of the specified type. Throws an exception if the factory does know how /// to get an instance of the specified type, but failed to do so. public static T TryGetInstance(this IFactory factory) where T : class => (T)factory.TryGetInstance(typeof(T)); /// /// Creates an instance with arguments. /// /// The type of the instance. /// The factory. /// Arguments. /// An instance of the specified type. /// /// Throws an exception if the factory failed to get an instance of the specified type. /// The arguments are used as dependencies by the factory. /// public static T CreateInstance(this IFactory factory, params object[] args) where T : class => (T)factory.CreateInstance(typeof(T), args); /// /// Creates an instance of a service, with arguments. /// /// /// The type of the instance. /// Named arguments. /// An instance of the specified type. /// /// The instance type does not need to be registered into the factory. /// The arguments are used as dependencies by the factory. Other dependencies /// are retrieved from the factory. /// public static object CreateInstance(this IFactory factory, Type type, params object[] args) { // LightInject has this, but then it requires RegisterConstructorDependency etc and has various oddities // including the most annoying one, which is that it does not work on singletons (hard to fix) //return factory.GetInstance(type, args); // this method is essentially used to build singleton instances, so it is assumed that it would be // more expensive to build and cache a dynamic method ctor than to simply invoke the ctor, as we do // here - this can be discussed // TODO: we currently try the ctor with most parameters, but we could want to fall back to others var ctor = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public).OrderByDescending(x => x.GetParameters().Length).FirstOrDefault(); if (ctor == null) throw new InvalidOperationException($"Could not find a public constructor for type {type.FullName}."); var ctorParameters = ctor.GetParameters(); var ctorArgs = new object[ctorParameters.Length]; var i = 0; foreach (var parameter in ctorParameters) { // no! IsInstanceOfType is not ok here // ReSharper disable once UseMethodIsInstanceOfType var arg = args?.FirstOrDefault(a => parameter.ParameterType.IsAssignableFrom(a.GetType())); ctorArgs[i++] = arg ?? factory.GetInstance(parameter.ParameterType); } return ctor.Invoke(ctorArgs); } } }