using System; using System.Threading; namespace Umbraco.Core.ObjectResolution { /// /// Represents the status of objects resolution. /// /// /// Before resolution is frozen it is possible to access its configuration, but not to get values. /// Once resolution is frozen, it is not possible to access its configuration anymore, but it is possible to get values. /// internal static class Resolution { private static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); /// /// Occurs when resolution is frozen. /// /// Occurs only once, since resolution can be frozen only once. public static event EventHandler Frozen; /// /// Gets or sets a value indicating whether resolution of objects is frozen. /// public static bool IsFrozen { get; private set; } public static void EnsureIsFrozen() { if (!IsFrozen) throw new InvalidOperationException("Resolution is not frozen, it is not yet possible to get values from it."); } /// /// Returns a disposable object that represents safe access to unfrozen resolution configuration. /// /// Should be used in a using(Resolution.Configuration) { ... } mode. public static IDisposable Configuration { get { IDisposable l = new WriteLock(_lock); if (Resolution.IsFrozen) { l.Dispose(); throw new InvalidOperationException("Resolution is frozen, it is not possible to configure it anymore."); } return l; } } /// /// Returns a disposable object that reprents dirty access to temporarily unfrozen resolution configuration. /// /// /// Should not be used. /// Should be used in a using(Resolution.DirtyBackdoorToConfiguration) { ... } mode. /// Because we just lift the frozen state, and we don't actually re-freeze, the Frozen event does not trigger. /// internal static IDisposable DirtyBackdoorToConfiguration { get { return new DirtyBackdoor(); } } // keep the class here because it needs write-access to Resolution.IsFrozen private class DirtyBackdoor : IDisposable { private static readonly System.Threading.ReaderWriterLockSlim _dirtyLock = new ReaderWriterLockSlim(); private IDisposable _lock; private bool _frozen; public DirtyBackdoor() { _lock = new WriteLock(_dirtyLock); _frozen = Resolution.IsFrozen; Resolution.IsFrozen = false; } public void Dispose() { Resolution.IsFrozen = _frozen; _lock.Dispose(); } } /// /// Freezes resolution. /// /// resolution is already frozen. public static void Freeze() { if (Resolution.IsFrozen) throw new InvalidOperationException("Resolution is frozen. It is not possible to freeze it again."); IsFrozen = true; if (Frozen != null) Frozen(null, null); } /// /// Resets resolution, ie unfreezes it and clears Frozen event. /// /// To be used in unit tests. internal static void Reset() { IsFrozen = false; Frozen = null; } } }