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; } } } } }