using System; using System.Collections.Generic; using System.Threading; using System.Web; namespace Umbraco.Core.Resolving { internal abstract class ManyObjectResolverBase where TResolved : class { private readonly bool _instancePerApplication = false; private List _applicationInstances = null; private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); #region Constructors /// /// Initializes a new instance of the class with an empty list of objects. /// /// If set to true will resolve singleton objects which will be created once for the lifetime of the application protected ManyObjectResolverBase(bool instancePerApplication = true) { _instancePerApplication = instancePerApplication; InstanceTypes = new List(); } /// /// Initializes a new instance of the class with an initial list of objects /// with creation of objects based on an HttpRequest lifetime scope. /// /// protected ManyObjectResolverBase(HttpContextBase httpContext) : this(false) { CurrentHttpContext = httpContext; } /// /// Initializes a new instance of the class with an initial list of objects. /// /// The list of objects. /// If set to true will resolve singleton objects which will be created once for the lifetime of the application protected ManyObjectResolverBase(IEnumerable value, bool instancePerApplication = true) { _instancePerApplication = instancePerApplication; InstanceTypes = new List(value); } /// /// Initializes a new instance of the class with an empty list of objects /// with creation of objects based on an HttpRequest lifetime scope. /// /// /// protected ManyObjectResolverBase(HttpContextBase httpContext, IEnumerable value) : this(value, false) { CurrentHttpContext = httpContext; } #endregion /// /// Returns the list of Types registered that instances will be created from /// protected List InstanceTypes { get; private set; } /// /// Returns the Current HttpContextBase used to construct this object if one exists. /// If one exists then the LifetimeScope will be ObjectLifetimeScope.HttpRequest /// protected HttpContextBase CurrentHttpContext { get; private set; } /// /// Returns the ObjectLifetimeScope for created objects /// protected ObjectLifetimeScope LifetimeScope { get { if (_instancePerApplication) return ObjectLifetimeScope.Application; if (CurrentHttpContext != null) return ObjectLifetimeScope.HttpRequest; return ObjectLifetimeScope.Transient; } } /// /// Returns the list of new object instances. /// protected IEnumerable Values { get { //we should not allow the returning/creating of objects if resolution is not yet frozen! if (Resolution.IsFrozen) throw new InvalidOperationException("Resolution is not frozen. It is not possible to instantiate and returng objects until resolution is frozen."); switch (LifetimeScope) { case ObjectLifetimeScope.HttpRequest: //create new instances per HttpContext, this means we'll lazily create them and once created, cache them in the HttpContext //create new instances per application, this means we'll lazily create them and once created, cache them using (var l = new UpgradeableReadLock(_lock)) { //check if the items contain the key (based on the full type name) if (CurrentHttpContext.Items[this.GetType().FullName] == null) { l.UpgradeToWriteLock(); //add the items to the context items (based on full type name) CurrentHttpContext.Items[this.GetType().FullName] = new List( PluginTypeResolver.Current.CreateInstances(InstanceTypes)); } return _applicationInstances; } case ObjectLifetimeScope.Application: //create new instances per application, this means we'll lazily create them and once created, cache them using(var l = new UpgradeableReadLock(_lock)) { if (_applicationInstances == null) { l.UpgradeToWriteLock(); _applicationInstances = new List( PluginTypeResolver.Current.CreateInstances(InstanceTypes)); } return _applicationInstances; } case ObjectLifetimeScope.Transient: default: //create new instances each time return PluginTypeResolver.Current.CreateInstances(InstanceTypes); } } } /// /// Removes a type. /// /// The type to remove. public void Remove(Type value) { Resolution.EnsureNotFrozen(); EnsureCorrectType(value); using (new WriteLock(_lock)) { InstanceTypes.Remove(value); } } /// /// Removes a type. /// /// public void Remove() { Remove(typeof (T)); } /// /// Adds a Type to the end of the list. /// /// The object to be added. public void Add(Type value) { Resolution.EnsureNotFrozen(); EnsureCorrectType(value); using (var l = new UpgradeableReadLock(_lock)) { if (InstanceTypes.Contains(value)) { throw new InvalidOperationException("The Type " + value + " already exists in the collection"); }; l.UpgradeToWriteLock(); InstanceTypes.Add(value); } } /// /// Adds a Type to the end of the list. /// /// public void Add() { Add(typeof (T)); } /// /// Clears the list. /// public void Clear() { Resolution.EnsureNotFrozen(); using (new WriteLock(_lock)) { InstanceTypes.Clear(); } } /// /// Inserts a Type at the specified index. /// /// The zero-based index at which the object should be inserted. /// The object to insert. public void Insert(int index, Type value) { Resolution.EnsureNotFrozen(); EnsureCorrectType(value); using (var l = new UpgradeableReadLock(_lock)) { if (InstanceTypes.Contains(value)) { throw new InvalidOperationException("The Type " + value + " already exists in the collection"); }; l.UpgradeToWriteLock(); InstanceTypes.Insert(index, value); } } /// /// Inserts a Type at the specified index. /// /// /// public void Insert(int index) { Insert(index, typeof (T)); } private void EnsureCorrectType(Type t) { if (!TypeHelper.IsTypeAssignableFrom(t)) throw new InvalidOperationException("The resolver " + this.GetType() + " can only accept types of " + typeof(TResolved) + ". The Type passed in to this method is " + t); } } }