Core.ObjectsResolution - cleanup

This commit is contained in:
Stephan
2013-01-23 16:44:38 -01:00
parent f466bc7cfb
commit b3e3855931
7 changed files with 272 additions and 198 deletions

View File

@@ -6,18 +6,17 @@ using System.Web;
namespace Umbraco.Core.ObjectResolution
{
/// <summary>
/// A base class for lazily resolving types for a resolver
/// </summary>
/// <typeparam name="TResolver"></typeparam>
/// <typeparam name="TResolved"></typeparam>
/// <remarks>
/// 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: This does not support Insert or Remove and therefore does not support any ordering unless
/// the types are marked with the WeightedPluginAttribute.
/// <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
@@ -25,105 +24,125 @@ namespace Umbraco.Core.ObjectResolution
{
#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>
/// Constructor accepting a list of lazy types
/// </summary>
/// <param name="listOfLazyTypes"></param>
/// <param name="scope"></param>
protected LazyManyObjectsResolverBase(IEnumerable<Lazy<Type>> listOfLazyTypes, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
/// <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(listOfLazyTypes);
AddTypes(lazyTypeList);
}
/// <summary>
/// Constructor accepting a delegate to return a list of types
/// </summary>
/// <param name="typeListDelegate"></param>
/// <param name="scope"></param>
protected LazyManyObjectsResolverBase(Func<IEnumerable<Type>> typeListDelegate, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
/// <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)
{
_listOfTypeListDelegates.Add(typeListDelegate);
_typeListProducerList.Add(typeListProducerList);
}
/// <summary>
/// Constructor accepting a list of lazy types
/// </summary>
/// <param name="httpContext"></param>
/// <param name="listOfLazyTypes"></param>
protected LazyManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable<Lazy<Type>> listOfLazyTypes)
/// <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(listOfLazyTypes);
AddTypes(lazyTypeList);
}
/// <summary>
/// Constructor accepting a delegate to return a list of types
/// </summary>
/// <param name="httpContext"></param>
/// <param name="typeListDelegate"></param>
protected LazyManyObjectsResolverBase(HttpContextBase httpContext, Func<IEnumerable<Type>> typeListDelegate)
/// <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)
{
_listOfTypeListDelegates.Add(typeListDelegate);
_typeListProducerList.Add(typeListProducerList);
}
#endregion
private readonly List<Lazy<Type>> _lazyTypeList = new List<Lazy<Type>>();
private readonly List<Func<IEnumerable<Type>>> _listOfTypeListDelegates = new List<Func<IEnumerable<Type>>>();
private readonly List<Func<IEnumerable<Type>>> _typeListProducerList = new List<Func<IEnumerable<Type>>>();
private List<Type> _resolvedTypes = null;
private readonly ReaderWriterLockSlim _typeResolutionLock = new ReaderWriterLockSlim();
private readonly ReaderWriterLockSlim _resolvedTypesLock = new ReaderWriterLockSlim();
/// <summary>
/// Used for unit tests
/// 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 { return _resolvedTypes != null; }
get
{
using (new ReadLock(_resolvedTypesLock))
{
return _resolvedTypes != null;
}
}
}
/// <summary>
/// Once this is called this will resolve all types registered in the lazy list
/// </summary>
protected override IEnumerable<Type> InstanceTypes
/// <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(_typeResolutionLock))
using (var lck = new UpgradeableReadLock(_resolvedTypesLock))
{
var lazyTypeList = _lazyTypeList.Select(x => x.Value).ToArray();
var listofTypeListDelegates = _listOfTypeListDelegates.SelectMany(x => x()).ToArray();
//we need to validate each resolved type now since we could not do it before when inserting the lazy delegates
if (!HasResolvedTypes)
if (_resolvedTypes == null)
{
lck.UpgradeToWriteLock();
// 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()));
lck.UpgradeToWriteLock();
_resolvedTypes = new List<Type>();
//first iterate the lazy type list
foreach (var l in lazyTypeList)
{
UpdateUniqueList(_resolvedTypes, l);
}
//next iterate the list of list type delegates
foreach (var l in listofTypeListDelegates)
{
UpdateUniqueList(_resolvedTypes, l);
}
// 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;
@@ -131,23 +150,28 @@ namespace Umbraco.Core.ObjectResolution
}
}
private void UpdateUniqueList(List<Type> uniqueList, Type toAdd)
// ensures that type is valid and not a duplicate
// then appends the type to the end of the list
private void AddValidAndNoDuplicate(List<Type> list, Type type)
{
EnsureCorrectType(toAdd);
if (uniqueList.Contains(toAdd))
EnsureCorrectType(type);
if (list.Contains(type))
{
throw new InvalidOperationException("The Type " + toAdd + " already exists in the collection");
}
uniqueList.Add(toAdd);
throw new InvalidOperationException(string.Format(
"Type {0} is already in the collection of types.", type.FullName));
}
list.Add(type);
}
/// <summary>
/// Allows adding of multiple lazy types at once
#region Types collection manipulation
/// <summary>
/// Lazily adds types from lazy types.
/// </summary>
/// <param name="types"></param>
/// <param name="types">The lazy types, to add.</param>
protected void AddTypes(IEnumerable<Lazy<Type>> types)
{
EnsureAddSupport();
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
@@ -160,27 +184,27 @@ namespace Umbraco.Core.ObjectResolution
}
/// <summary>
/// Adds a type list delegate to the collection
/// Lazily adds types from a function producing types.
/// </summary>
/// <param name="typeListDelegate"></param>
public void AddTypeListDelegate(Func<IEnumerable<Type>> typeListDelegate)
/// <param name="typeListProducer">The functions producing types, to add.</param>
public void AddTypeListDelegate(Func<IEnumerable<Type>> typeListProducer)
{
EnsureAddSupport();
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
{
_listOfTypeListDelegates.Add(typeListDelegate);
_typeListProducerList.Add(typeListProducer);
}
}
/// <summary>
/// Adds a lazy type to the list
/// Lazily adds a type from a lazy type.
/// </summary>
/// <param name="value"></param>
/// <param name="value">The lazy type, to add.</param>
public void AddType(Lazy<Type> value)
{
EnsureAddSupport();
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
@@ -190,9 +214,10 @@ namespace Umbraco.Core.ObjectResolution
}
/// <summary>
/// Converts the static type added to a lazy type and adds it to the internal list
/// Lazily adds a type from an actual type.
/// </summary>
/// <param name="value"></param>
/// <param name="types">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));
@@ -203,7 +228,7 @@ namespace Umbraco.Core.ObjectResolution
/// </summary>
public override void Clear()
{
EnsureClearSupport();
EnsureSupportsClear();
using (Resolution.Configuration)
using (GetWriteLock())
@@ -212,8 +237,12 @@ namespace Umbraco.Core.ObjectResolution
}
}
/// <summary>
/// Does not support removal
#endregion
#region Types collection manipulation support
/// <summary>
/// Gets a <c>false</c> value indicating that the resolver does NOT support removing types.
/// </summary>
protected override bool SupportsRemove
{
@@ -221,11 +250,13 @@ namespace Umbraco.Core.ObjectResolution
}
/// <summary>
/// Does not support insert
/// Gets a <c>false</c> value indicating that the resolver does NOT support inserting types.
/// </summary>
protected override bool SupportsInsert
{
get { return false; }
}
}
}
#endregion
}
}

View File

@@ -27,18 +27,17 @@ namespace Umbraco.Core.ObjectResolution
#region Constructors
/// <summary>
/// Constructor
/// Initializes a new instance of the <see cref="LegacyTransientObjectsResolver{TResolver, TResolved}"/> class with an initial list of object types.
/// </summary>
/// <param name="types"></param>
/// <param name="value">A function returning the list of object types.</param>
/// <remarks>
/// We are creating Transient instances (new instances each time) because this is how the legacy code worked and
/// I don't want to muck anything up by changing them to application based instances.
/// TODO: However, it would make much more sense to do this and would speed up the application plus this would make the GetById method much easier.
/// </remarks>
protected LegacyTransientObjectsResolver(Func<IEnumerable<Type>> types)
: base(types, ObjectLifetimeScope.Transient) // new objects every time
{
}
protected LegacyTransientObjectsResolver(Func<IEnumerable<Type>> value)
: base(value, ObjectLifetimeScope.Transient) // new objects every time
{ }
#endregion
/// <summary>

View File

@@ -25,7 +25,8 @@ namespace Umbraco.Core.ObjectResolution
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects.
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects,
/// and an optional 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>
@@ -65,7 +66,8 @@ namespace Umbraco.Core.ObjectResolution
}
/// <summary>
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of object types.
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of object types,
/// and an optional lifetime scope.
/// </summary>
/// <param name="value">The list of object types.</param>
/// <param name="scope">The lifetime scope of instantiated objects, default is per Application.</param>
@@ -215,18 +217,6 @@ namespace Umbraco.Core.ObjectResolution
#region Types collection manipulation
/// <summary>
/// Ensures that a type is a valid type for the resolver.
/// </summary>
/// <param name="value">The type to test.</param>
/// <exception cref="InvalidOperationException">the type is not a valid type for the resolver.</exception>
protected void EnsureCorrectType(Type value)
{
if (!TypeHelper.IsTypeAssignableFrom<TResolved>(value))
throw new InvalidOperationException(string.Format(
"Type {0} is not an acceptable type for resolver {1}.", value.FullName, this.GetType().FullName));
}
/// <summary>
/// Removes a type.
/// </summary>
@@ -235,7 +225,7 @@ namespace Umbraco.Core.ObjectResolution
/// the type is not a valid type for the resolver.</exception>
public virtual void RemoveType(Type value)
{
EnsureRemoveSupport();
EnsureSupportsRemove();
using (Resolution.Configuration)
using (var l = new UpgradeableReadLock(_lock))
@@ -268,7 +258,7 @@ namespace Umbraco.Core.ObjectResolution
/// a type is not a valid type for the resolver, or a type is already in the collection of types.</exception>
protected void AddTypes(IEnumerable<Type> types)
{
EnsureAddSupport();
EnsureSupportsAdd();
using (Resolution.Configuration)
using (new WriteLock(_lock))
@@ -276,7 +266,7 @@ namespace Umbraco.Core.ObjectResolution
foreach(var t in types)
{
EnsureCorrectType(t);
if (InstanceTypes.Contains(t))
if (_instanceTypes.Contains(t))
{
throw new InvalidOperationException(string.Format(
"Type {0} is already in the collection of types.", t.FullName));
@@ -295,13 +285,13 @@ namespace Umbraco.Core.ObjectResolution
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
public virtual void AddType(Type value)
{
EnsureAddSupport();
EnsureSupportsAdd();
using (Resolution.Configuration)
using (var l = new UpgradeableReadLock(_lock))
{
EnsureCorrectType(value);
if (InstanceTypes.Contains(value))
if (_instanceTypes.Contains(value))
{
throw new InvalidOperationException(string.Format(
"Type {0} is already in the collection of types.", value.FullName));
@@ -331,7 +321,7 @@ namespace Umbraco.Core.ObjectResolution
/// <exception cref="InvalidOperationException">the resolver does not support clearing types.</exception>
public virtual void Clear()
{
EnsureClearSupport();
EnsureSupportsClear();
using (Resolution.Configuration)
using (new WriteLock(_lock))
@@ -350,13 +340,13 @@ namespace Umbraco.Core.ObjectResolution
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
public virtual void InsertType(int index, Type value)
{
EnsureInsertSupport();
EnsureSupportsInsert();
using (Resolution.Configuration)
using (var l = new UpgradeableReadLock(_lock))
{
EnsureCorrectType(value);
if (InstanceTypes.Contains(value))
if (_instanceTypes.Contains(value))
{
throw new InvalidOperationException(string.Format(
"Type {0} is already in the collection of types.", value.FullName));
@@ -389,24 +379,24 @@ namespace Umbraco.Core.ObjectResolution
/// or the new type is already in the collection of types.</exception>
public virtual void InsertTypeBefore(Type existingType, Type value)
{
EnsureInsertSupport();
EnsureSupportsInsert();
using (Resolution.Configuration)
using (var l = new UpgradeableReadLock(_lock))
{
EnsureCorrectType(existingType);
EnsureCorrectType(value);
if (!InstanceTypes.Contains(existingType))
if (!_instanceTypes.Contains(existingType))
{
throw new InvalidOperationException(string.Format(
"Type {0} is not in the collection of types.", existingType.FullName));
}
if (InstanceTypes.Contains(value))
if (_instanceTypes.Contains(value))
{
throw new InvalidOperationException(string.Format(
"Type {0} is already in the collection of types.", value.FullName));
}
int index = InstanceTypes.IndexOf(existingType);
int index = _instanceTypes.IndexOf(existingType);
l.UpgradeToWriteLock();
_instanceTypes.Insert(index, value);
@@ -463,60 +453,95 @@ namespace Umbraco.Core.ObjectResolution
return new WriteLock(_lock);
}
/// <summary>
/// Throws an exception if this does not support Remove
/// </summary>
protected void EnsureRemoveSupport()
#region Type utilities
/// <summary>
/// Ensures that a type is a valid type for the resolver.
/// </summary>
/// <param name="value">The type to test.</param>
/// <exception cref="InvalidOperationException">the type is not a valid type for the resolver.</exception>
protected void EnsureCorrectType(Type value)
{
if (!TypeHelper.IsTypeAssignableFrom<TResolved>(value))
throw new InvalidOperationException(string.Format(
"Type {0} is not an acceptable type for resolver {1}.", value.FullName, this.GetType().FullName));
}
#endregion
#region Types collection manipulation support
/// <summary>
/// Ensures that the resolver supports removing types.
/// </summary>
/// <exception cref="InvalidOperationException">The resolver does not support removing types.</exception>
protected void EnsureSupportsRemove()
{
if (!SupportsRemove)
throw new InvalidOperationException("This resolver does not support Removing types");
throw new InvalidOperationException("This resolver does not support removing types");
}
/// <summary>
/// Throws an exception if this does not support Clear
/// </summary>
protected void EnsureClearSupport()
{
/// <summary>
/// Ensures that the resolver supports clearing types.
/// </summary>
/// <exception cref="InvalidOperationException">The resolver does not support clearing types.</exception>
protected void EnsureSupportsClear() {
if (!SupportsClear)
throw new InvalidOperationException("This resolver does not support Clearing types");
throw new InvalidOperationException("This resolver does not support clearing types");
}
/// <summary>
/// Throws an exception if this does not support Add
/// </summary>
protected void EnsureAddSupport()
/// <summary>
/// Ensures that the resolver supports adding types.
/// </summary>
/// <exception cref="InvalidOperationException">The resolver does not support adding types.</exception>
protected void EnsureSupportsAdd()
{
if (!SupportsAdd)
throw new InvalidOperationException("This resolver does not support Adding new types");
throw new InvalidOperationException("This resolver does not support adding new types");
}
/// <summary>
/// Throws an exception if this does not support insert
/// </summary>
protected void EnsureInsertSupport()
/// <summary>
/// Ensures that the resolver supports inserting types.
/// </summary>
/// <exception cref="InvalidOperationException">The resolver does not support inserting types.</exception>
protected void EnsureSupportsInsert()
{
if (!SupportsInsert)
throw new InvalidOperationException("This resolver does not support Inserting new types");
throw new InvalidOperationException("This resolver does not support inserting new types");
}
/// <summary>
/// Gets a value indicating whether the resolver supports adding types.
/// </summary>
protected virtual bool SupportsAdd
{
get { return true; }
}
protected virtual bool SupportsInsert
/// <summary>
/// Gets a value indicating whether the resolver supports inserting types.
/// </summary>
protected virtual bool SupportsInsert
{
get { return true; }
}
protected virtual bool SupportsClear
/// <summary>
/// Gets a value indicating whether the resolver supports clearing types.
/// </summary>
protected virtual bool SupportsClear
{
get { return true; }
}
protected virtual bool SupportsRemove
/// <summary>
/// Gets a value indicating whether the resolver supports removing types.
/// </summary>
protected virtual bool SupportsRemove
{
get { return true; }
}
}
}
#endregion
}
}