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-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-07-27 14:06:41 -02: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-07-27 05:52:01 +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-07-27 05:52:01 +06:00
}
2012-08-01 10:48:19 +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 10:48:19 +06:00
/// with creation of objects based on an HttpRequest lifetime scope.
/// </summary>
/// <param name="httpContext"></param>
2012-08-01 11:24:39 +06:00
protected ManyObjectsResolverBase ( HttpContextBase httpContext )
2012-08-01 10:48:19 +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 ;
CurrentHttpContext = httpContext ;
2013-01-23 07:45:00 +03:00
_instanceTypes = new List < Type >();
2012-08-01 10:48:19 +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 initial list of objects.
2012-08-01 10:48:19 +06:00
/// </summary>
/// <param name="value">The list of objects.</param>
/// <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-08-01 10:48:19 +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.
/// </summary>
/// <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-08-01 09:49:10 +06:00
}
}
/// <summary>
/// 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 ();
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
{
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 ();
2012-10-13 10:59:21 +05:00
2013-01-23 07:45:00 +03:00
EnsureResolutionNotFrozen ();
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
}
}