using System;
using System.Threading;
namespace Umbraco.Core.ObjectResolution
{
///
/// The base class for all single-object resolvers.
///
/// The type of the concrete resolver class.
/// The type of the resolved object.
///
/// Resolves "single" objects ie objects for which there is only one application-wide instance, such as the MVC Controller factory.
///
public abstract class SingleObjectResolverBase : ResolverBase
where TResolved : class
where TResolver : ResolverBase
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private readonly bool _canBeNull;
private TResolved _value;
#region Constructors
///
/// Initialize a new instance of the class.
///
/// By default CanBeNull is false, so Value has to be initialized before being read,
/// otherwise an exception will be thrown when reading it.
protected SingleObjectResolverBase()
: this(false)
{ }
///
/// Initialize a new instance of the class with an instance of the resolved object.
///
/// An instance of the resolved object.
/// By default CanBeNull is false, so value has to be non-null, or Value has to be
/// initialized before being accessed, otherwise an exception will be thrown when reading it.
protected SingleObjectResolverBase(TResolved value)
: this(false)
{
_value = value;
}
///
/// Initialize a new instance of the class with a value indicating whether the resolved object instance can be null.
///
/// A value indicating whether the resolved object instance can be null.
/// If CanBeNull is false, Value has to be initialized before being read,
/// otherwise an exception will be thrown when reading it.
protected SingleObjectResolverBase(bool canBeNull)
{
_canBeNull = canBeNull;
}
///
/// Initialize a new instance of the class with an instance of the resolved object,
/// and a value indicating whether that instance can be null.
///
/// An instance of the resolved object.
/// A value indicating whether the resolved object instance can be null.
/// If CanBeNull is false, value has to be non-null, or Value has to be initialized before being read,
/// otherwise an exception will be thrown when reading it.
protected SingleObjectResolverBase(TResolved value, bool canBeNull)
{
_value = value;
_canBeNull = canBeNull;
}
#endregion
///
/// Gets or sets a value indicating whether the resolver can resolve objects before resolution is frozen.
///
/// This is false by default and is used for some special internal resolvers.
internal bool CanResolveBeforeFrozen { get; set; }
///
/// Gets a value indicating whether the resolved object instance can be null.
///
public bool CanBeNull
{
get { return _canBeNull; }
}
///
/// Gets a value indicating whether the resolved object instance is null.
///
public bool HasValue
{
get { return _value != null; }
}
///
/// Gets or sets the resolved object instance.
///
///
/// value is set to null, but cannot be null (CanBeNull is false).
/// value is read and is null, but cannot be null (CanBeNull is false),
/// or value is set (read) and resolution is (not) frozen.
protected TResolved Value
{
get
{
using (Resolution.Reader(CanResolveBeforeFrozen))
using (new ReadLock(_lock))
{
if (!_canBeNull && _value == null)
throw new InvalidOperationException(string.Format(
"Resolver {0} requires a value, and none was supplied.", this.GetType().FullName));
return _value;
}
}
set
{
using (Resolution.Configuration)
using (var l = new UpgradeableReadLock(_lock))
{
if (!_canBeNull && value == null)
throw new ArgumentNullException("value");
l.UpgradeToWriteLock();
_value = value;
}
}
}
}
}