Files
Umbraco-CMS/src/Umbraco.Core/ObjectResolution/LazyManyObjectsResolverbase.cs
Shannon Deminick 164f22aa50 Makes ApplicationContext disposable so that 3rd party devs are able to reset their applications (or unit tests), updated
resolution and created a ResolverCollection to track all resolvers created so it's super easy to reset all of them at once
(which is what happens on ApplicationContext.Dispose. ApplicationContext.Dispose is also implicitly implemented as to not
show in intellisense that the Dispose method exists... must be cast to IDisposable to work.
2013-03-12 01:50:56 +04:00

272 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
namespace Umbraco.Core.ObjectResolution
{
/// <summary>
/// The base class for all lazy many-objects resolvers.
/// </summary>
/// <typeparam name="TResolver">The type of the concrete resolver class.</typeparam>
/// <typeparam name="TResolved">The type of the resolved objects.</typeparam>
/// <remarks>
/// <para>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.</para>
/// <para>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.</para>
/// </remarks>
internal abstract class LazyManyObjectsResolverBase<TResolver, TResolved> : ManyObjectsResolverBase<TResolver, TResolved>
where TResolved : class
where TResolver : ResolverBase
{
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects,
/// with creation of objects based on an HttpRequest lifetime scope.
/// </summary>
/// <param name="scope">The lifetime scope of instantiated objects, default is per Application.</param>
/// <remarks>If <paramref name="scope"/> is per HttpRequest then there must be a current HttpContext.</remarks>
/// <exception cref="InvalidOperationException"><paramref name="scope"/> is per HttpRequest but the current HttpContext is null.</exception>
protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: base(scope)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects,
/// with creation of objects based on an HttpRequest lifetime scope.
/// </summary>
/// <param name="httpContext">The HttpContextBase corresponding to the HttpRequest.</param>
/// <exception cref="ArgumentNullException"><paramref name="httpContext"/> is <c>null</c>.</exception>
protected LazyManyObjectsResolverBase(HttpContextBase httpContext)
: base(httpContext)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list
/// <remarks>If <paramref name="scope"/> is per HttpRequest then there must be a current HttpContext.</remarks>
/// <exception cref="InvalidOperationException"><paramref name="scope"/> is per HttpRequest but the current HttpContext is null.</exception>
protected LazyManyObjectsResolverBase(IEnumerable<Lazy<Type>> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: this(scope)
{
AddTypes(lazyTypeList);
}
/// <summary>
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list
/// of functions producing types, and an optional lifetime scope.
/// </summary>
/// <param name="typeListProducerList">The list of functions producing types.</param>
/// <param name="scope">The lifetime scope of instantiated objects, default is per Application.</param>
/// <remarks>If <paramref name="scope"/> is per HttpRequest then there must be a current HttpContext.</remarks>
/// <exception cref="InvalidOperationException"><paramref name="scope"/> is per HttpRequest but the current HttpContext is null.</exception>
protected LazyManyObjectsResolverBase(Func<IEnumerable<Type>> typeListProducerList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: this(scope)
{
_typeListProducerList.Add(typeListProducerList);
}
/// <summary>
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of
/// lazy object types, with creation of objects based on an HttpRequest lifetime scope.
/// </summary>
/// <param name="httpContext">The HttpContextBase corresponding to the HttpRequest.</param>
/// <param name="lazyTypeList">The list of lazy object types.</param>
/// <exception cref="ArgumentNullException"><paramref name="httpContext"/> is <c>null</c>.</exception>
protected LazyManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable<Lazy<Type>> lazyTypeList)
: this(httpContext)
{
AddTypes(lazyTypeList);
}
/// <summary>
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of
/// functions producing types, with creation of objects based on an HttpRequest lifetime scope.
/// </summary>
/// <param name="httpContext">The HttpContextBase corresponding to the HttpRequest.</param>
/// <param name="typeListProducerList">The list of functions producing types.</param>
/// <exception cref="ArgumentNullException"><paramref name="httpContext"/> is <c>null</c>.</exception>
protected LazyManyObjectsResolverBase(HttpContextBase httpContext, Func<IEnumerable<Type>> typeListProducerList)
: this(httpContext)
{
_typeListProducerList.Add(typeListProducerList);
}
#endregion
private readonly List<Lazy<Type>> _lazyTypeList = new List<Lazy<Type>>();
private readonly List<Func<IEnumerable<Type>>> _typeListProducerList = new List<Func<IEnumerable<Type>>>();
private readonly List<Type> _excludedTypesList = new List<Type>();
private List<Type> _resolvedTypes = null;
private readonly ReaderWriterLockSlim _resolvedTypesLock = new ReaderWriterLockSlim();
/// <summary>
/// Gets a value indicating whether the resolver has resolved types to create instances from.
/// </summary>
/// <remarks>To be used in unit tests.</remarks>
internal bool HasResolvedTypes
{
get
{
using (new ReadLock(_resolvedTypesLock))
{
return _resolvedTypes != null;
}
}
}
/// <summary>
/// Gets the list of types to create instances from.
/// </summary>
/// <remarks>When called, will get the types from the lazy list.</remarks>
protected override IEnumerable<Type> InstanceTypes
{
get
{
using (var lck = new UpgradeableReadLock(_resolvedTypesLock))
{
if (_resolvedTypes == null)
{
lck.UpgradeToWriteLock();
_resolvedTypes = new List<Type>();
// get the types by evaluating the lazy & producers
var types = new List<Type>();
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)))
{
AddValidAndNoDuplicate(_resolvedTypes, type);
}
}
return _resolvedTypes;
}
}
}
/// <summary>
/// Ensures that type is valid and not a duplicate
/// then appends the type to the end of the list
/// </summary>
/// <param name="list"></param>
/// <param name="type"></param>
private void AddValidAndNoDuplicate(List<Type> 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
/// <summary>
/// Removes types from the list of types, once it has been lazily evaluated, and before actual objects are instanciated.
/// </summary>
/// <param name="value">The type to remove.</param>
public override void RemoveType(Type value)
{
EnsureSupportsRemove();
_excludedTypesList.Add(value);
}
/// <summary>
/// Lazily adds types from lazy types.
/// </summary>
/// <param name="types">The lazy types, to add.</param>
protected void AddTypes(IEnumerable<Lazy<Type>> types)
{
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
{
foreach (var t in types)
{
_lazyTypeList.Add(t);
}
}
}
/// <summary>
/// Lazily adds types from a function producing types.
/// </summary>
/// <param name="typeListProducer">The functions producing types, to add.</param>
public void AddTypeListDelegate(Func<IEnumerable<Type>> typeListProducer)
{
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
{
_typeListProducerList.Add(typeListProducer);
}
}
/// <summary>
/// Lazily adds a type from a lazy type.
/// </summary>
/// <param name="value">The lazy type, to add.</param>
public void AddType(Lazy<Type> value)
{
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
{
_lazyTypeList.Add(value);
}
}
/// <summary>
/// Lazily adds a type from an actual type.
/// </summary>
/// <param name="value">The actual type, to add.</param>
/// <remarks>The type is converted to a lazy type.</remarks>
public override void AddType(Type value)
{
AddType(new Lazy<Type>(() => value));
}
/// <summary>
/// Clears all lazy types
/// </summary>
public override void Clear()
{
EnsureSupportsClear();
using (Resolution.Configuration)
using (GetWriteLock())
{
_lazyTypeList.Clear();
}
}
#endregion
#region Types collection manipulation support
/// <summary>
/// Gets a <c>false</c> value indicating that the resolver does NOT support inserting types.
/// </summary>
protected override bool SupportsInsert
{
get { return false; }
}
#endregion
}
}