using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;
using Umbraco.Core.Logging;
namespace Umbraco.Core.ObjectResolution
{
///
/// The base class for all lazy many-objects resolvers.
///
/// The type of the concrete resolver class.
/// The type of the resolved objects.
///
/// 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: it does not support Insert or Remove and therefore does not support any ordering unless
/// the types are marked with the WeightAttribute.
///
public abstract class LazyManyObjectsResolverBase : ManyObjectsResolverBase
where TResolved : class
where TResolver : ResolverBase
{
#region Constructors
///
/// Initializes a new instance of the class with an empty list of objects,
/// and an optional lifetime scope.
///
///
///
/// The lifetime scope of instantiated objects, default is per Application.
/// If is per HttpRequest then there must be a current HttpContext.
/// is per HttpRequest but the current HttpContext is null.
protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: base(serviceProvider, logger, scope)
{
Initialize();
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use ctor specifying IServiceProvider instead")]
protected LazyManyObjectsResolverBase(ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: base(scope)
{
Initialize();
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use ctor specifying IServiceProvider instead")]
protected LazyManyObjectsResolverBase(HttpContextBase httpContext)
: base(httpContext)
{
Initialize();
}
protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, IEnumerable> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: this(serviceProvider, logger, scope)
{
AddTypes(lazyTypeList);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use ctor specifying IServiceProvider instead")]
protected LazyManyObjectsResolverBase(IEnumerable> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, lazyTypeList, scope)
{
}
protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, Func> typeListProducerList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: this(serviceProvider, logger, scope)
{
_typeListProducerList.Add(typeListProducerList);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use ctor specifying IServiceProvider instead")]
protected LazyManyObjectsResolverBase(Func> typeListProducerList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, typeListProducerList, scope)
{
}
protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext, IEnumerable> lazyTypeList)
: this(serviceProvider, logger, httpContext)
{
AddTypes(lazyTypeList);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use ctor specifying IServiceProvider instead")]
protected LazyManyObjectsResolverBase(HttpContextBase httpContext, IEnumerable> lazyTypeList)
: this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext, lazyTypeList)
{
}
protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext, Func> typeListProducerList)
: this(serviceProvider, logger, httpContext)
{
_typeListProducerList.Add(typeListProducerList);
}
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use ctor specifying IServiceProvider instead")]
protected LazyManyObjectsResolverBase(HttpContextBase httpContext, Func> typeListProducerList)
: this(new ActivatorServiceProvider(), LoggerResolver.Current.Logger, httpContext, typeListProducerList)
{
}
#endregion
private readonly List> _lazyTypeList = new List>();
private readonly List>> _typeListProducerList = new List>>();
private readonly List _excludedTypesList = new List();
private Lazy> _resolvedTypes;
///
/// Initializes a new instance of the class with an empty list of objects,
/// with creation of objects based on an HttpRequest lifetime scope.
///
///
///
/// The HttpContextBase corresponding to the HttpRequest.
/// is null.
protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, HttpContextBase httpContext)
: base(serviceProvider, logger, httpContext)
{
}
///
/// Initializes a new instance of the class with an initial list of object types,
/// and an optional lifetime scope.
///
///
///
/// The list of object types.
/// The lifetime scope of instantiated objects, default is per Application.
/// If is per HttpRequest then there must be a current HttpContext.
/// is per HttpRequest but the current HttpContext is null.
protected LazyManyObjectsResolverBase(IServiceProvider serviceProvider, ILogger logger, IEnumerable value, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: base(serviceProvider, logger, value, scope)
{
}
private void Initialize()
{
_resolvedTypes = new Lazy>(() =>
{
var resolvedTypes = new List();
// get the types by evaluating the lazy & producers
var types = new List();
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) == false))
{
AddValidAndNoDuplicate(resolvedTypes, type);
}
return resolvedTypes;
});
}
///
/// Gets a value indicating whether the resolver has resolved types to create instances from.
///
/// To be used in unit tests.
public bool HasResolvedTypes
{
get { return _resolvedTypes.IsValueCreated; }
}
///
/// Gets the list of types to create instances from.
///
/// When called, will get the types from the lazy list.
protected override IEnumerable InstanceTypes
{
get { return _resolvedTypes.Value; }
}
///
/// Ensures that type is valid and not a duplicate
/// then appends the type to the end of the list
///
///
///
private void AddValidAndNoDuplicate(List 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
///
/// Removes types from the list of types, once it has been lazily evaluated, and before actual objects are instanciated.
///
/// The type to remove.
public override void RemoveType(Type value)
{
EnsureSupportsRemove();
_excludedTypesList.Add(value);
}
///
/// Lazily adds types from lazy types.
///
/// The lazy types, to add.
protected void AddTypes(IEnumerable> types)
{
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
{
foreach (var t in types)
{
_lazyTypeList.Add(t);
}
}
}
///
/// Lazily adds types from a function producing types.
///
/// The functions producing types, to add.
public void AddTypeListDelegate(Func> typeListProducer)
{
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
{
_typeListProducerList.Add(typeListProducer);
}
}
///
/// Lazily adds a type from a lazy type.
///
/// The lazy type, to add.
public void AddType(Lazy value)
{
EnsureSupportsAdd();
using (Resolution.Configuration)
using (GetWriteLock())
{
_lazyTypeList.Add(value);
}
}
///
/// Lazily adds a type from an actual type.
///
/// The actual type, to add.
/// The type is converted to a lazy type.
public override void AddType(Type value)
{
AddType(new Lazy(() => value));
}
///
/// Clears all lazy types
///
public override void Clear()
{
EnsureSupportsClear();
using (Resolution.Configuration)
using (GetWriteLock())
{
_lazyTypeList.Clear();
}
}
#endregion
#region Types collection manipulation support
///
/// Gets a false value indicating that the resolver does NOT support inserting types.
///
protected override bool SupportsInsert
{
get { return false; }
}
#endregion
}
}