using System; using System.Collections.Generic; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; namespace Umbraco.Core.Composing { /// /// Represents a composition. /// /// Although a composition exposes the application's service container, people should use the /// extension methods (such as PropertyEditors() or SetPublishedContentModelFactory()) and /// avoid accessing the container. This is because everything needs to be properly registered and with /// the proper lifecycle. These methods will take care of it. Directly registering into the container /// may cause issues. public class Composition : IRegister { private readonly Dictionary _builders = new Dictionary(); private readonly Dictionary> _uniques = new Dictionary>(); private readonly IRegister _register; /// /// Initializes a new instance of the class. /// /// A register. /// A type loader. /// A logger. /// The runtime state. /// Optional configs. /// An IOHelper public Composition(IRegister register, TypeLoader typeLoader, IProfilingLogger logger, IRuntimeState runtimeState, Configs configs, IIOHelper ioHelper, AppCaches appCaches) { _register = register; TypeLoader = typeLoader; Logger = logger; RuntimeState = runtimeState; Configs = configs; IOHelper = ioHelper; AppCaches = appCaches; } #region Services /// /// Gets the logger. /// public IProfilingLogger Logger { get; } public IIOHelper IOHelper { get; } public AppCaches AppCaches { get; } /// /// Gets the type loader. /// public TypeLoader TypeLoader { get; } /// /// Gets the runtime state. /// public IRuntimeState RuntimeState { get; } /// /// Gets the configurations. /// public Configs Configs { get; } #endregion #region IRegister /// public object Concrete => _register.Concrete; /// public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient) => _register.Register(serviceType, lifetime); /// public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient) => _register.Register(serviceType, implementingType, lifetime); /// public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) where TService : class => _register.Register(factory, lifetime); /// public void Register(Type serviceType, object instance) => _register.Register(serviceType, instance); /// public void RegisterFor(Lifetime lifetime = Lifetime.Transient) where TService : class => _register.RegisterFor(lifetime); /// public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) where TService : class => _register.RegisterFor(implementingType, lifetime); /// public void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) where TService : class => _register.RegisterFor(factory, lifetime); /// public void RegisterFor(TService instance) where TService : class => _register.RegisterFor(instance); /// public void RegisterAuto(Type serviceBaseType) => _register.RegisterAuto(serviceBaseType); /// public void ConfigureForWeb() => _register.ConfigureForWeb(); /// public IFactory CreateFactory() { foreach (var onCreating in OnCreatingFactory.Values) onCreating(); foreach (var unique in _uniques.Values) unique(_register); _uniques.Clear(); // no point keep them around foreach (var builder in _builders.Values) builder.RegisterWith(_register); _builders.Clear(); // no point keep them around IFactory factory = null; Configs.RegisterWith(_register, () => factory); // ReSharper disable once AccessToModifiedClosure -- on purpose _register.Register(_ => factory, Lifetime.Singleton); factory = _register.CreateFactory(); return factory; } /// /// Gets a dictionary of action to execute when creating the factory. /// public Dictionary OnCreatingFactory { get; } = new Dictionary(); #endregion #region Unique private string GetUniqueName() => GetUniqueName(typeof(TService)); private string GetUniqueName(Type serviceType) => serviceType.FullName; private string GetUniqueName() => GetUniqueName(typeof(TService), typeof(TTarget)); private string GetUniqueName(Type serviceType, Type targetType) => serviceType.FullName + "::" + targetType.FullName; /// /// Registers a unique service as its own implementation. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Type serviceType) => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, Lifetime.Singleton); /// /// Registers a unique service with an implementation type. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Type serviceType, Type implementingType) => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, implementingType, Lifetime.Singleton); /// /// Registers a unique service with an implementation factory. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Func factory) where TService : class => _uniques[GetUniqueName()] = register => register.Register(factory, Lifetime.Singleton); /// /// Registers a unique service with an implementing instance. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Type serviceType, object instance) => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, instance); /// /// Registers a unique service for a target, as its own implementation. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUniqueFor() where TService : class => _uniques[GetUniqueName()] = register => register.RegisterFor(Lifetime.Singleton); /// /// Registers a unique service for a target, with an implementing type. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUniqueFor(Type implementingType) where TService : class => _uniques[GetUniqueName()] = register => register.RegisterFor(implementingType, Lifetime.Singleton); /// /// Registers a unique service for a target, with an implementation factory. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUniqueFor(Func factory) where TService : class => _uniques[GetUniqueName()] = register => register.RegisterFor(factory, Lifetime.Singleton); /// /// Registers a unique service for a target, with an implementing instance. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUniqueFor(TService instance) where TService : class => _uniques[GetUniqueName()] = register => register.RegisterFor(instance); #endregion #region Collection Builders /// /// Gets a collection builder (and registers the collection). /// /// The type of the collection builder. /// The collection builder. public TBuilder WithCollectionBuilder() where TBuilder: ICollectionBuilder, new() { var typeOfBuilder = typeof(TBuilder); if (_builders.TryGetValue(typeOfBuilder, out var o)) return (TBuilder) o; var builder = new TBuilder(); _builders[typeOfBuilder] = builder; return builder; } #endregion } }