using System; using System.Collections.Generic; using System.Threading; using System.Web; namespace Umbraco.Core.ObjectResolution { internal abstract class ManyObjectsResolverBase : ResolverBase where TResolved : class where TResolver : class { 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. /// /// The lifetime scope of instantiated objects, default is per Application protected ManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application) { if (scope == ObjectLifetimeScope.HttpRequest) { if (HttpContext.Current == null) { throw new InvalidOperationException("Use alternative constructor accepting a HttpContextBase object in order to set the lifetime scope to HttpRequest when HttpContext.Current is null"); } CurrentHttpContext = new HttpContextWrapper(HttpContext.Current); } LifetimeScope = scope; InstanceTypes = new List(); } /// /// Initializes a new instance of the class with an empty list of objects. /// with creation of objects based on an HttpRequest lifetime scope. /// /// protected ManyObjectsResolverBase(HttpContextBase httpContext) { LifetimeScope = ObjectLifetimeScope.HttpRequest; CurrentHttpContext = httpContext; InstanceTypes = new List(); } /// /// 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 ManyObjectsResolverBase(IEnumerable value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application) : this(scope) { InstanceTypes = new List(value); } /// /// Initializes a new instance of the class with an initial list of objects /// with creation of objects based on an HttpRequest lifetime scope. /// /// /// protected ManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable value) : this(httpContext) { InstanceTypes = new List(value); } #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; private set; } /// /// Returns the list of new object instances. /// protected IEnumerable Values { get { //We cannot return values unless resolution is locked if (!Resolution.IsFrozen) throw new InvalidOperationException("Values cannot be returned 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(CreateInstances()); } return (List)CurrentHttpContext.Items[this.GetType().FullName]; } 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(CreateInstances()); } return _applicationInstances; } case ObjectLifetimeScope.Transient: default: //create new instances each time return CreateInstances(); } } } protected virtual IEnumerable CreateInstances() { return PluginManager.Current.CreateInstances(InstanceTypes); } /// /// Removes a type. /// /// The type to remove. public void RemoveType(Type value) { EnsureCorrectType(value); using (new WriteLock(_lock)) { InstanceTypes.Remove(value); } } /// /// Removes a type. /// /// public void RemoveType() { RemoveType(typeof (T)); } /// /// Adds a Type to the end of the list. /// /// The object to be added. public void AddType(Type value) { 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 AddType() { AddType(typeof (T)); } /// /// Clears the list. /// public void Clear() { 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 InsertType(int index, Type value) { 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 InsertType(int index) { InsertType(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); } } }