using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Web; 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 WeightedPluginAttribute. /// internal abstract class LazyManyObjectsResolverBase : ManyObjectsResolverBase where TResolved : class where TResolver : class { #region Constructors /// /// Initializes a new instance of the class with an empty list of objects, /// with creation of objects based on an HttpRequest 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(ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : base(scope) { } /// /// 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(HttpContextBase httpContext) : base(httpContext) { } /// /// Initializes a new instance of the class with an initial list /// If is per HttpRequest then there must be a current HttpContext. /// is per HttpRequest but the current HttpContext is null. protected LazyManyObjectsResolverBase(IEnumerable> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(scope) { AddTypes(lazyTypeList); } /// /// Initializes a new instance of the class with an initial list /// of functions producing types, and an optional lifetime scope. /// /// The list of functions producing 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(Func> typeListProducerList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(scope) { _typeListProducerList.Add(typeListProducerList); } /// /// Initializes a new instance of the class with an initial list of /// lazy object types, with creation of objects based on an HttpRequest lifetime scope. /// /// The HttpContextBase corresponding to the HttpRequest. /// The list of lazy object types. /// is null. protected LazyManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable> lazyTypeList) : this(httpContext) { AddTypes(lazyTypeList); } /// /// Initializes a new instance of the class with an initial list of /// functions producing types, with creation of objects based on an HttpRequest lifetime scope. /// /// The HttpContextBase corresponding to the HttpRequest. /// The list of functions producing types. /// is null. protected LazyManyObjectsResolverBase(HttpContextBase httpContext, Func> typeListProducerList) : this(httpContext) { _typeListProducerList.Add(typeListProducerList); } #endregion private readonly List> _lazyTypeList = new List>(); private readonly List>> _typeListProducerList = new List>>(); private List _resolvedTypes = null; private readonly ReaderWriterLockSlim _resolvedTypesLock = new ReaderWriterLockSlim(); /// /// Gets a value indicating whether the resolver has resolved types to create instances from. /// /// To be used in unit tests. internal bool HasResolvedTypes { get { using (new ReadLock(_resolvedTypesLock)) { return _resolvedTypes != null; } } } /// /// Gets the list of types to create instances from. /// /// When called, will get the types from the lazy list. protected override IEnumerable InstanceTypes { get { using (var lck = new UpgradeableReadLock(_resolvedTypesLock)) { if (_resolvedTypes == null) { // 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())); lck.UpgradeToWriteLock(); _resolvedTypes = new List(); // we need to validate each resolved type now since we could // not do it before evaluating the lazy & producers foreach (var type in types) AddValidAndNoDuplicate(_resolvedTypes, type); } return _resolvedTypes; } } } // 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 /// /// 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 removing types. /// protected override bool SupportsRemove { get { return false; } } /// /// Gets a false value indicating that the resolver does NOT support inserting types. /// protected override bool SupportsInsert { get { return false; } } #endregion } }