using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Web; using Umbraco.Core.Logging; namespace Umbraco.Core.ObjectResolution { /// /// The base class for all lazy many-objects resolvers. /// /// The type of the concrete resolver class. /// The type of the resolved objects. /// /// This is a special case resolver for when types get lazily resolved in order to resolve the actual types. This is useful /// for when there is some processing overhead (i.e. Type finding in assemblies) to return the Types used to instantiate the instances. /// In some these cases we don't want to have to type-find during application startup, only when we need to resolve the instances. /// Important notes about this resolver: it does not support Insert or Remove and therefore does not support any ordering unless /// the types are marked with the WeightAttribute. /// public abstract class LazyManyObjectsResolverBase : ManyObjectsResolverBase where TResolved : class where TResolver : ResolverBase { #region Constructors /// /// Initializes a new instance of the class with an empty list of objects, /// and an optional lifetime scope. /// /// /// /// The lifetime scope of instantiated objects, default is per Application. /// If is per HttpRequest then there must be a current HttpContext. /// is per HttpRequest but the current HttpContext is null. protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : base(serviceProvider, logger, scope) { Initialize(); } [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Use ctor specifying IServiceProvider instead")] protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : base(scope) { Initialize(); } [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Use ctor specifying IServiceProvider instead")] protected LazyManyObjectsResolverBase(HttpContextBase httpContext) : base(httpContext) { Initialize(); } protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, IEnumerable> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(serviceProvider, logger, scope) { AddTypes(lazyTypeList); } [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Use ctor specifying IServiceProvider instead")] protected LazyManyObjectsResolverBase(IEnumerable> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, lazyTypeList, scope) { } protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, Func> typeListProducerList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(serviceProvider, logger, scope) { _typeListProducerList.Add(typeListProducerList); } [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Use ctor specifying IServiceProvider instead")] protected LazyManyObjectsResolverBase(Func> typeListProducerList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, typeListProducerList, scope) { } protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext, IEnumerable> lazyTypeList) : this(serviceProvider, logger, httpContext) { AddTypes(lazyTypeList); } [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Use ctor specifying IServiceProvider instead")] protected LazyManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable> lazyTypeList) : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext, lazyTypeList) { } protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext, Func> typeListProducerList) : this(serviceProvider, logger, httpContext) { _typeListProducerList.Add(typeListProducerList); } [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Use ctor specifying IServiceProvider instead")] protected LazyManyObjectsResolverBase(HttpContextBase httpContext, Func> typeListProducerList) : this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext, typeListProducerList) { } #endregion private readonly List> _lazyTypeList = new List>(); private readonly List>> _typeListProducerList = new List>>(); private readonly List _excludedTypesList = new List(); private Lazy> _resolvedTypes; /// /// Initializes a new instance of the class with an empty list of objects, /// with creation of objects based on an HttpRequest lifetime scope. /// /// /// /// The HttpContextBase corresponding to the HttpRequest. /// is null. protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext) : base(serviceProvider, logger, httpContext) { } /// /// Initializes a new instance of the class with an initial list of object types, /// and an optional lifetime scope. /// /// /// /// The list of object types. /// The lifetime scope of instantiated objects, default is per Application. /// If is per HttpRequest then there must be a current HttpContext. /// is per HttpRequest but the current HttpContext is null. protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, IEnumerable value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : base(serviceProvider, logger, value, scope) { } private void Initialize() { _resolvedTypes = new Lazy>(() => { var resolvedTypes = new List(); // get the types by evaluating the lazy & producers var types = new List(); types.AddRange(_lazyTypeList.Select(x => x.Value)); types.AddRange(_typeListProducerList.SelectMany(x => x())); // we need to validate each resolved type now since we could // not do it before evaluating the lazy & producers foreach (var type in types.Where(x => _excludedTypesList.Contains(x) == false)) { AddValidAndNoDuplicate(resolvedTypes, type); } return resolvedTypes; }); } /// /// Gets a value indicating whether the resolver has resolved types to create instances from. /// /// To be used in unit tests. public bool HasResolvedTypes { get { return _resolvedTypes.IsValueCreated; } } /// /// Gets the list of types to create instances from. /// /// When called, will get the types from the lazy list. protected override IEnumerable InstanceTypes { get { return _resolvedTypes.Value; } } /// /// Ensures that type is valid and not a duplicate /// then appends the type to the end of the list /// /// /// private void AddValidAndNoDuplicate(List list, Type type) { EnsureCorrectType(type); if (list.Contains(type)) { throw new InvalidOperationException(string.Format( "Type {0} is already in the collection of types.", type.FullName)); } list.Add(type); } #region Types collection manipulation /// /// Removes types from the list of types, once it has been lazily evaluated, and before actual objects are instanciated. /// /// The type to remove. public override void RemoveType(Type value) { EnsureSupportsRemove(); _excludedTypesList.Add(value); } /// /// Lazily adds types from lazy types. /// /// The lazy types, to add. protected void AddTypes(IEnumerable> types) { EnsureSupportsAdd(); using (Resolution.Configuration) using (GetWriteLock()) { foreach (var t in types) { _lazyTypeList.Add(t); } } } /// /// Lazily adds types from a function producing types. /// /// The functions producing types, to add. public void AddTypeListDelegate(Func> typeListProducer) { EnsureSupportsAdd(); using (Resolution.Configuration) using (GetWriteLock()) { _typeListProducerList.Add(typeListProducer); } } /// /// Lazily adds a type from a lazy type. /// /// The lazy type, to add. public void AddType(Lazy value) { EnsureSupportsAdd(); using (Resolution.Configuration) using (GetWriteLock()) { _lazyTypeList.Add(value); } } /// /// Lazily adds a type from an actual type. /// /// The actual type, to add. /// The type is converted to a lazy type. public override void AddType(Type value) { AddType(new Lazy(() => value)); } /// /// Clears all lazy types /// public override void Clear() { EnsureSupportsClear(); using (Resolution.Configuration) using (GetWriteLock()) { _lazyTypeList.Clear(); } } #endregion #region Types collection manipulation support /// /// Gets a false value indicating that the resolver does NOT support inserting types. /// protected override bool SupportsInsert { get { return false; } } #endregion } }