2012-07-31 01:06:10 +06:00
using System ;
2012-07-27 05:52:01 +06:00
using System.Collections.Generic ;
2012-09-25 11:06:32 +07:00
using System.Linq ;
2012-07-27 05:52:01 +06:00
using System.Threading ;
2012-08-01 09:49:10 +06:00
using System.Web ;
2012-07-27 05:52:01 +06:00
2012-08-10 13:18:13 +06:00
namespace Umbraco.Core.ObjectResolution
2012-07-27 05:52:01 +06:00
{
2012-08-01 22:06:15 +06:00
internal abstract class ManyObjectsResolverBase < TResolver , TResolved > : ResolverBase < TResolver >
where TResolved : class
where TResolver : class
2012-07-27 05:52:01 +06:00
{
2012-08-01 09:49:10 +06:00
private List < TResolved > _applicationInstances = null ;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim ( ) ;
2013-01-23 07:45:00 +03:00
private readonly List < Type > _instanceTypes = new List < Type > ( ) ;
2012-08-01 09:49:10 +06:00
#region Constructors
2012-08-14 12:03:34 +06:00
2012-08-01 09:49:10 +06:00
/// <summary>
2012-08-01 22:06:15 +06:00
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects.
2012-08-01 09:49:10 +06:00
/// </summary>
2012-08-01 10:48:19 +06:00
/// <param name="scope">The lifetime scope of instantiated objects, default is per Application</param>
2012-08-01 11:24:39 +06:00
protected ManyObjectsResolverBase ( ObjectLifetimeScope scope = ObjectLifetimeScope . Application )
2012-08-01 09:49:10 +06:00
{
2012-08-14 12:03:34 +06:00
CanResolveBeforeFrozen = false ;
2012-08-01 10:48:19 +06:00
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 ;
2013-01-23 07:45:00 +03:00
_instanceTypes = new List < Type > ( ) ;
2012-08-01 09:49:10 +06:00
}
2012-07-27 05:52:01 +06:00
2012-07-27 14:06:41 -02:00
/// <summary>
2012-08-01 22:06:15 +06:00
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an empty list of objects.
2012-08-01 09:49:10 +06:00
/// with creation of objects based on an HttpRequest lifetime scope.
2012-07-27 14:06:41 -02:00
/// </summary>
2012-08-01 09:49:10 +06:00
/// <param name="httpContext"></param>
2012-08-01 11:24:39 +06:00
protected ManyObjectsResolverBase ( HttpContextBase httpContext )
2012-07-27 05:52:01 +06:00
{
2012-08-14 12:03:34 +06:00
CanResolveBeforeFrozen = false ;
if ( httpContext = = null ) throw new ArgumentNullException ( "httpContext" ) ;
2012-08-01 10:48:19 +06:00
LifetimeScope = ObjectLifetimeScope . HttpRequest ;
2012-08-01 09:49:10 +06:00
CurrentHttpContext = httpContext ;
2013-01-23 07:45:00 +03:00
_instanceTypes = new List < Type > ( ) ;
2012-07-27 05:52:01 +06:00
}
2012-07-27 14:06:41 -02:00
/// <summary>
2012-08-01 22:06:15 +06:00
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of objects.
2012-07-27 14:06:41 -02:00
/// </summary>
2012-07-31 01:06:10 +06:00
/// <param name="value">The list of objects.</param>
2012-08-01 10:48:19 +06:00
/// <param name="scope">If set to true will resolve singleton objects which will be created once for the lifetime of the application</param>
2012-08-01 11:24:39 +06:00
protected ManyObjectsResolverBase ( IEnumerable < Type > value , ObjectLifetimeScope scope = ObjectLifetimeScope . Application )
2012-08-01 10:48:19 +06:00
: this ( scope )
2013-01-23 07:45:00 +03:00
{
_instanceTypes = new List < Type > ( value ) ;
2012-07-27 14:06:41 -02:00
}
2012-07-27 05:52:01 +06:00
2012-07-27 14:06:41 -02:00
/// <summary>
2012-08-01 22:06:15 +06:00
/// Initializes a new instance of the <see cref="ManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list of objects
2012-08-01 09:49:10 +06:00
/// with creation of objects based on an HttpRequest lifetime scope.
/// </summary>
/// <param name="httpContext"></param>
/// <param name="value"></param>
2012-08-01 11:24:39 +06:00
protected ManyObjectsResolverBase ( HttpContextBase httpContext , IEnumerable < Type > value )
2012-08-01 10:48:19 +06:00
: this ( httpContext )
2012-08-01 09:49:10 +06:00
{
2013-01-23 07:45:00 +03:00
_instanceTypes = new List < Type > ( value ) ;
2012-08-01 09:49:10 +06:00
}
#endregion
2012-08-14 12:03:34 +06:00
/// <summary>
/// used internally for special resolvers to be able to resolve objects before resolution is frozen.
/// </summary>
internal bool CanResolveBeforeFrozen { get ; set ; }
2012-08-01 09:49:10 +06:00
/// <summary>
/// Returns the list of Types registered that instances will be created from
/// </summary>
2013-01-23 07:45:00 +03:00
protected virtual IEnumerable < Type > InstanceTypes
{
get { return _instanceTypes ; }
}
2012-08-01 09:49:10 +06:00
/// <summary>
/// Returns the Current HttpContextBase used to construct this object if one exists.
/// If one exists then the LifetimeScope will be ObjectLifetimeScope.HttpRequest
/// </summary>
protected HttpContextBase CurrentHttpContext { get ; private set ; }
/// <summary>
/// Returns the ObjectLifetimeScope for created objects
/// </summary>
2012-08-01 10:48:19 +06:00
protected ObjectLifetimeScope LifetimeScope { get ; private set ; }
2012-08-01 09:49:10 +06:00
2012-09-25 11:06:32 +07:00
private int _defaultPluginWeight = 10 ;
2013-01-23 07:45:00 +03:00
2012-09-25 11:06:32 +07:00
/// <summary>
/// Used in conjunction with GetSortedValues and WeightedPluginAttribute, if any of the objects
/// being resolved do not contain the WeightedPluginAttribute then this will be the default weight applied
/// to the object.
/// </summary>
protected virtual int DefaultPluginWeight
{
get { return _defaultPluginWeight ; }
set { _defaultPluginWeight = value ; }
}
/// <summary>
/// If a resolver requries that objects are resolved with a specific order using the WeightedPluginAttribute
/// then this method should be used instead of the Values property.
/// </summary>
/// <returns></returns>
protected IEnumerable < TResolved > GetSortedValues ( )
{
var vals = Values . ToList ( ) ;
//ensure they are sorted
vals . Sort ( ( f1 , f2 ) = >
{
Func < object , int > getWeight = o = >
{
var weightAttribute = f1 . GetType ( ) . GetCustomAttribute < WeightedPluginAttribute > ( true ) ;
return weightAttribute ! = null ? weightAttribute . Weight : DefaultPluginWeight ;
} ;
return getWeight ( f1 ) . CompareTo ( getWeight ( f2 ) ) ;
} ) ;
return vals ;
}
2012-08-01 09:49:10 +06:00
/// <summary>
/// Returns the list of new object instances.
2012-07-27 14:06:41 -02:00
/// </summary>
protected IEnumerable < TResolved > Values
2012-07-27 05:52:01 +06:00
{
2012-08-01 09:49:10 +06:00
get
2012-08-01 10:15:39 +06:00
{
2012-08-01 22:06:15 +06:00
//We cannot return values unless resolution is locked
2012-08-14 12:03:34 +06:00
if ( ! CanResolveBeforeFrozen & & ! Resolution . IsFrozen )
2012-08-01 22:06:15 +06:00
throw new InvalidOperationException ( "Values cannot be returned until Resolution is frozen" ) ;
2012-08-01 09:49:10 +06:00
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)
2012-08-01 23:30:37 +06:00
CurrentHttpContext . Items [ this . GetType ( ) . FullName ] = new List < TResolved > ( CreateInstances ( ) ) ;
2012-08-01 09:49:10 +06:00
}
2012-08-01 10:48:19 +06:00
return ( List < TResolved > ) CurrentHttpContext . Items [ this . GetType ( ) . FullName ] ;
2012-08-01 09:49:10 +06:00
}
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 ( ) ;
2012-08-01 23:30:37 +06:00
_applicationInstances = new List < TResolved > ( CreateInstances ( ) ) ;
2012-08-01 09:49:10 +06:00
}
return _applicationInstances ;
}
case ObjectLifetimeScope . Transient :
default :
//create new instances each time
2012-08-01 23:30:37 +06:00
return CreateInstances ( ) ;
2012-08-01 09:49:10 +06:00
}
}
2012-07-27 05:52:01 +06:00
}
2012-08-01 23:30:37 +06:00
protected virtual IEnumerable < TResolved > CreateInstances ( )
{
return PluginManager . Current . CreateInstances < TResolved > ( InstanceTypes ) ;
}
2012-07-27 14:06:41 -02:00
/// <summary>
2012-08-01 09:49:10 +06:00
/// Removes a type.
2012-07-27 14:06:41 -02:00
/// </summary>
2012-08-01 09:49:10 +06:00
/// <param name="value">The type to remove.</param>
2013-01-23 07:45:00 +03:00
public virtual void RemoveType ( Type value )
2012-10-13 10:59:21 +05:00
{
2013-01-23 07:45:00 +03:00
EnsureRemoveSupport ( ) ;
2012-10-13 10:59:21 +05:00
2013-01-23 07:45:00 +03:00
EnsureResolutionNotFrozen ( ) ;
using ( GetWriteLock ( ) )
2012-08-01 09:49:10 +06:00
{
2012-10-13 10:59:21 +05:00
EnsureCorrectType ( value ) ;
2013-01-23 07:45:00 +03:00
_instanceTypes . Remove ( value ) ;
2012-07-27 05:52:01 +06:00
}
}
2012-07-27 14:06:41 -02:00
/// <summary>
2012-08-01 09:49:10 +06:00
/// Removes a type.
/// </summary>
/// <typeparam name="T"></typeparam>
2012-08-01 10:48:19 +06:00
public void RemoveType < T > ( )
2012-08-01 09:49:10 +06:00
{
2012-08-01 10:48:19 +06:00
RemoveType ( typeof ( T ) ) ;
2012-08-01 09:49:10 +06:00
}
2012-10-13 10:59:21 +05:00
/// <summary>
/// protected method allow the inheritor to add many types at once
/// </summary>
/// <param name="types"></param>
protected void AddTypes ( IEnumerable < Type > types )
2013-01-23 07:45:00 +03:00
{
EnsureAddSupport ( ) ;
EnsureResolutionNotFrozen ( ) ;
using ( GetWriteLock ( ) )
2012-10-13 10:59:21 +05:00
{
foreach ( var t in types )
{
EnsureCorrectType ( t ) ;
if ( InstanceTypes . Contains ( t ) )
{
throw new InvalidOperationException ( "The Type " + t + " already exists in the collection" ) ;
} ;
2013-01-23 07:45:00 +03:00
_instanceTypes . Add ( t ) ;
2012-10-13 10:59:21 +05:00
}
}
}
2012-08-01 09:49:10 +06:00
/// <summary>
/// Adds a Type to the end of the list.
2012-07-27 14:06:41 -02:00
/// </summary>
/// <param name="value">The object to be added.</param>
2013-01-23 07:45:00 +03:00
public virtual void AddType ( Type value )
2012-07-27 05:52:01 +06:00
{
2013-01-23 07:45:00 +03:00
EnsureAddSupport ( ) ;
EnsureResolutionNotFrozen ( ) ;
2012-10-13 10:59:21 +05:00
2013-01-23 07:45:00 +03:00
using ( GetWriteLock ( ) )
2012-07-27 05:52:01 +06:00
{
2012-10-13 10:59:21 +05:00
EnsureCorrectType ( value ) ;
2012-08-01 09:49:10 +06:00
if ( InstanceTypes . Contains ( value ) )
2012-07-31 01:06:10 +06:00
{
2012-08-01 09:49:10 +06:00
throw new InvalidOperationException ( "The Type " + value + " already exists in the collection" ) ;
2012-07-31 01:06:10 +06:00
} ;
2013-01-23 07:45:00 +03:00
_instanceTypes . Add ( value ) ;
2012-07-27 06:02:41 +06:00
}
2012-07-27 05:52:01 +06:00
}
2012-08-01 09:49:10 +06:00
/// <summary>
/// Adds a Type to the end of the list.
/// </summary>
/// <typeparam name="T"></typeparam>
2012-08-01 10:48:19 +06:00
public void AddType < T > ( )
2012-08-01 09:49:10 +06:00
{
2012-08-01 10:48:19 +06:00
AddType ( typeof ( T ) ) ;
2012-08-01 09:49:10 +06:00
}
2012-07-27 14:06:41 -02:00
/// <summary>
/// Clears the list.
/// </summary>
2013-01-23 07:45:00 +03:00
public virtual void Clear ( )
2012-07-27 05:52:01 +06:00
{
2013-01-23 07:45:00 +03:00
EnsureClearSupport ( ) ;
2012-10-13 10:59:21 +05:00
2013-01-23 07:45:00 +03:00
EnsureResolutionNotFrozen ( ) ;
using ( GetWriteLock ( ) )
2012-07-27 05:52:01 +06:00
{
2013-01-23 07:45:00 +03:00
_instanceTypes . Clear ( ) ;
2012-07-27 06:02:41 +06:00
}
2012-07-27 05:52:01 +06:00
}
2012-07-27 14:06:41 -02:00
/// <summary>
2012-08-01 09:49:10 +06:00
/// Inserts a Type at the specified index.
2012-07-27 14:06:41 -02:00
/// </summary>
/// <param name="index">The zero-based index at which the object should be inserted.</param>
/// <param name="value">The object to insert.</param>
2013-01-23 07:45:00 +03:00
public virtual void InsertType ( int index , Type value )
2012-10-13 10:59:21 +05:00
{
2013-01-23 07:45:00 +03:00
EnsureInsertSupport ( ) ;
EnsureResolutionNotFrozen ( ) ;
2012-10-13 10:59:21 +05:00
2013-01-23 07:45:00 +03:00
using ( var l = GetWriteLock ( ) )
2012-07-27 05:52:01 +06:00
{
2012-10-13 10:59:21 +05:00
EnsureCorrectType ( value ) ;
2012-08-01 09:49:10 +06:00
if ( InstanceTypes . Contains ( value ) )
2012-07-31 01:06:10 +06:00
{
2012-08-01 09:49:10 +06:00
throw new InvalidOperationException ( "The Type " + value + " already exists in the collection" ) ;
2012-07-31 01:06:10 +06:00
} ;
2013-01-23 07:45:00 +03:00
_instanceTypes . Insert ( index , value ) ;
2012-07-27 06:02:41 +06:00
}
2012-07-27 05:52:01 +06:00
}
2012-08-01 09:49:10 +06:00
/// <summary>
/// Inserts a Type at the specified index.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="index"></param>
2012-08-01 10:48:19 +06:00
public void InsertType < T > ( int index )
2012-08-01 09:49:10 +06:00
{
2012-08-01 10:48:19 +06:00
InsertType ( index , typeof ( T ) ) ;
2012-08-01 09:49:10 +06:00
}
2013-01-23 07:45:00 +03:00
/// <summary>
/// Returns a WriteLock to use when modifying collections
/// </summary>
/// <returns></returns>
protected WriteLock GetWriteLock ( )
{
return new WriteLock ( _lock ) ;
}
2013-01-23 18:40:40 +03:00
2013-01-23 07:45:00 +03:00
/// <summary>
/// Throws an exception if resolution is frozen
/// </summary>
protected void EnsureResolutionNotFrozen ( )
{
if ( Resolution . IsFrozen )
throw new InvalidOperationException ( "The type list cannot be modified after resolution has been frozen" ) ;
}
/// <summary>
/// Throws an exception if this does not support Remove
/// </summary>
protected void EnsureRemoveSupport ( )
{
if ( ! SupportsRemove )
throw new InvalidOperationException ( "This resolver does not support Removing types" ) ;
}
/// <summary>
/// Throws an exception if this does not support Clear
/// </summary>
protected void EnsureClearSupport ( )
{
if ( ! SupportsClear )
throw new InvalidOperationException ( "This resolver does not support Clearing types" ) ;
}
/// <summary>
/// Throws an exception if this does not support Add
/// </summary>
protected void EnsureAddSupport ( )
{
if ( ! SupportsAdd )
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 ( )
{
if ( ! SupportsInsert )
throw new InvalidOperationException ( "This resolver does not support Inserting new types" ) ;
}
/// <summary>
/// Throws an exception if the type is not of the TResolved type
/// </summary>
/// <param name="t"></param>
protected void EnsureCorrectType ( Type t )
2012-08-01 09:49:10 +06:00
{
if ( ! TypeHelper . IsTypeAssignableFrom < TResolved > ( t ) )
throw new InvalidOperationException ( "The resolver " + this . GetType ( ) + " can only accept types of " + typeof ( TResolved ) + ". The Type passed in to this method is " + t ) ;
}
2012-10-13 10:59:21 +05:00
protected virtual bool SupportsAdd
{
2013-01-23 07:45:00 +03:00
get { return true ; }
2012-10-13 10:59:21 +05:00
}
protected virtual bool SupportsInsert
{
2013-01-23 07:45:00 +03:00
get { return true ; }
2012-10-13 10:59:21 +05:00
}
protected virtual bool SupportsClear
{
2013-01-23 07:45:00 +03:00
get { return true ; }
2012-10-13 10:59:21 +05:00
}
protected virtual bool SupportsRemove
{
2013-01-23 07:45:00 +03:00
get { return true ; }
2012-10-13 10:59:21 +05:00
}
2012-07-27 05:52:01 +06:00
}
}