diff --git a/src/Umbraco.Core/Scoping/IScopeContext.cs b/src/Umbraco.Core/Scoping/IScopeContext.cs new file mode 100644 index 0000000000..73a04751b9 --- /dev/null +++ b/src/Umbraco.Core/Scoping/IScopeContext.cs @@ -0,0 +1,42 @@ +using System; + +namespace Umbraco.Core.Scoping +{ + /// + /// Represents a scope context. + /// + /// A scope context can enlist objects that will be attached to the scope, and available + /// for the duration of the scope. In addition, it can enlist actions, that will run when the + /// scope is exiting, and after the database transaction has been commited. + public interface IScopeContext + { + /// + /// Enlists an action. + /// + /// The action unique identifier. + /// The action. + /// The optional action priority (default is 100, lower runs first). + /// + /// It is ok to enlist multiple action with the same key but only the first one will run. + /// The action boolean parameter indicates whether the scope completed or not. + /// + void Enlist(string key, Action action, int priority = 100); + + /// + /// Enlists an object and action. + /// + /// The type of the object. + /// The object unique identifier. + /// A function providing the object. + /// The optional action. + /// The optional action priority (default is 100, lower runs first). + /// The object. + /// + /// On the first time an object is enlisted with a given key, the object is actually + /// created. Next calls just return the existing object. It is ok to enlist multiple objects + /// and action with the same key but only the first one is used, the others are ignored. + /// The action boolean parameter indicates whether the scope completed or not. + /// + T Enlist(string key, Func creator, Action action = null, int priority = 100); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Scoping/IScopeInternal.cs b/src/Umbraco.Core/Scoping/IScopeInternal.cs deleted file mode 100644 index 220b6456a7..0000000000 --- a/src/Umbraco.Core/Scoping/IScopeInternal.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Data; -using Umbraco.Core.Events; -using Umbraco.Core.Persistence; - -namespace Umbraco.Core.Scoping -{ - /// - /// Provides additional, internal scope functionnalities. - /// - internal interface IScopeInternal : IScope // fixme - define what's internal and why - { - /// - /// Gets the parent scope, if any, or null. - /// - IScopeInternal ParentScope { get; } - - /// - /// Gets a value indicating whether this scope should be registered in - /// call context even when an http context is available. - /// - bool CallContext { get; } - - /// - /// Gets the scope transaction isolation level. - /// - IsolationLevel IsolationLevel { get; } - - /// - /// Gets the scope database, if any, else null. - /// - IUmbracoDatabase DatabaseOrNull { get; } - - /// - /// Get the scope event messages, if any, else null. - /// - EventMessages MessagesOrNull { get; } - - /// - /// Gets a value indicating whether filesystems are scoped. - /// - bool ScopedFileSystems { get; } - - /// - /// Registers that a child has completed. - /// - /// The child's completion status. - /// Completion status can be true (completed), false (could not complete), or null (not properly exited). - void ChildCompleted(bool? completed); - - /// - /// Resets the scope. - /// - /// Reset completion to "unspecified". - void Reset(); - } -} diff --git a/src/Umbraco.Core/Scoping/IScopeProvider.cs b/src/Umbraco.Core/Scoping/IScopeProvider.cs index 3be0f73a98..fa8cbf72e5 100644 --- a/src/Umbraco.Core/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/IScopeProvider.cs @@ -74,7 +74,7 @@ namespace Umbraco.Core.Scoping /// /// Gets the scope context. /// - ScopeContext Context { get; } + IScopeContext Context { get; } #if DEBUG_SCOPES Dictionary CallContextObjects { get; } diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 51084f026a..dbeb5f1d0d 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Scoping /// Implements . /// /// Not thread-safe obviously. - internal class Scope : IScopeInternal + internal class Scope : IScope { // fixme // considering that a great amount of things here are only useful for the top-level @@ -182,12 +182,12 @@ namespace Umbraco.Core.Scoping public bool Detachable { get; } // the parent scope (in a nested scopes chain) - public IScopeInternal ParentScope { get; set; } + public Scope ParentScope { get; set; } public bool Attached { get; set; } // the original scope (when attaching a detachable scope) - public IScopeInternal OrigScope { get; set; } + public Scope OrigScope { get; set; } // the original context (when attaching a detachable scope) public ScopeContext OrigContext { get; set; } diff --git a/src/Umbraco.Core/Scoping/ScopeContext.cs b/src/Umbraco.Core/Scoping/ScopeContext.cs index 6172fb51a3..97b655ed63 100644 --- a/src/Umbraco.Core/Scoping/ScopeContext.cs +++ b/src/Umbraco.Core/Scoping/ScopeContext.cs @@ -4,10 +4,7 @@ using System.Linq; namespace Umbraco.Core.Scoping { - // fixme should we have an IScopeContext? - // fixme document all this properly! - - public class ScopeContext : IInstanceIdentifiable + internal class ScopeContext : IScopeContext, IInstanceIdentifiable { private Dictionary _enlisted; private bool _exiting; @@ -62,7 +59,7 @@ namespace Umbraco.Core.Scoping public T Item { get; } - public int Priority { get; private set; } + public int Priority { get; } public void Execute(bool completed) { @@ -70,38 +67,19 @@ namespace Umbraco.Core.Scoping } } - // todo: replace with optional parameters when we can break things - public T Enlist(string key, Func creator) - { - return Enlist(key, creator, null, 100); - } - - // todo: replace with optional parameters when we can break things - public T Enlist(string key, Func creator, Action action) - { - return Enlist(key, creator, action, 100); - } - - // todo: replace with optional parameters when we can break things - public void Enlist(string key, Action action) - { - Enlist(key, null, (completed, item) => action(completed), 100); - } - - public void Enlist(string key, Action action, int priority) + public void Enlist(string key, Action action, int priority = 100) { Enlist(key, null, (completed, item) => action(completed), priority); } - public T Enlist(string key, Func creator, Action action, int priority) + public T Enlist(string key, Func creator, Action action = null, int priority = 100) { if (_exiting) throw new InvalidOperationException("Cannot enlist now, context is exiting."); var enlistedObjects = _enlisted ?? (_enlisted = new Dictionary()); - IEnlistedObject enlisted; - if (enlistedObjects.TryGetValue(key, out enlisted)) + if (enlistedObjects.TryGetValue(key, out IEnlistedObject enlisted)) { var enlistedAs = enlisted as EnlistedObject; if (enlistedAs == null) throw new InvalidOperationException("An item with the key already exists, but with a different type."); diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index b23c162bd4..b8fdb25119 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -3,7 +3,6 @@ using System.Collections; using System.Collections.Generic; using System.Data; using System.Runtime.Remoting.Messaging; -using System.Text; using System.Web; using Umbraco.Core.Composing; using Umbraco.Core.Events; @@ -39,7 +38,7 @@ namespace Umbraco.Core.Scoping SafeCallContext.Register( () => { - var scope = GetCallContextObject(ScopeItemKey); + var scope = GetCallContextObject(ScopeItemKey); var context = GetCallContextObject(ContextItemKey); SetCallContextObject(ScopeItemKey, null); SetCallContextObject(ContextItemKey, null); @@ -48,12 +47,12 @@ namespace Umbraco.Core.Scoping o => { // cannot re-attached over leaked scope/context - if (GetCallContextObject(ScopeItemKey) != null) + if (GetCallContextObject(ScopeItemKey) != null) throw new Exception("Found leaked scope when restoring call context."); if (GetCallContextObject(ContextItemKey) != null) throw new Exception("Found leaked context when restoring call context."); - var t = (Tuple) o; + var t = (Tuple) o; SetCallContextObject(ScopeItemKey, t.Item1); SetCallContextObject(ContextItemKey, t.Item2); }); @@ -231,7 +230,7 @@ namespace Umbraco.Core.Scoping internal const string ContextItemKey = "Umbraco.Core.Scoping.ScopeContext"; - internal static ScopeContext AmbientContextInternal + internal static ScopeContext AmbientContextStatic { get { @@ -253,7 +252,7 @@ namespace Umbraco.Core.Scoping } /// - public ScopeContext AmbientContext => AmbientContextInternal; + public ScopeContext AmbientContext => AmbientContextStatic; #endregion @@ -266,13 +265,13 @@ namespace Umbraco.Core.Scoping // fixme - more weird static - we should try to get rid of all static & use an accessor private static readonly ScopeReference StaticScopeReference = new ScopeReference(new ScopeProvider(null, null, null)); - internal static IScopeInternal AmbientScopeInternal + private static Scope AmbientScopeStatic { get { // try http context, fallback onto call context - var value = GetHttpContextObject(ScopeItemKey, false); - return value ?? GetCallContextObject(ScopeItemKey); + var value = GetHttpContextObject(ScopeItemKey, false); + return value ?? GetCallContextObject(ScopeItemKey); } set { @@ -291,15 +290,15 @@ namespace Umbraco.Core.Scoping } /// - public IScopeInternal AmbientScope + public Scope AmbientScope { - get => AmbientScopeInternal; - internal set => AmbientScopeInternal = value; + get => AmbientScopeStatic; + set => AmbientScopeStatic = value; } #endregion - public void SetAmbient(IScopeInternal scope, ScopeContext context = null) + public void SetAmbient(Scope scope, ScopeContext context = null) { // clear all SetHttpContextObject(ScopeItemKey, null, false); @@ -415,7 +414,7 @@ namespace Umbraco.Core.Scoping } /// - public ScopeContext Context => AmbientContext; + public IScopeContext Context => AmbientContext; #if DEBUG_SCOPES // this code needs TLC diff --git a/src/Umbraco.Core/Scoping/ScopeReference.cs b/src/Umbraco.Core/Scoping/ScopeReference.cs index 6f378e2abb..e1bfc21adc 100644 --- a/src/Umbraco.Core/Scoping/ScopeReference.cs +++ b/src/Umbraco.Core/Scoping/ScopeReference.cs @@ -19,7 +19,7 @@ { // dispose the entire chain (if any) // reset (don't commit by default) - IScopeInternal scope; + Scope scope; while ((scope = _scopeProvider.AmbientScope) != null) { scope.Reset(); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 1ac25a0c3f..c379959400 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1253,7 +1253,7 @@ - + diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index a4e13cc6f7..bf7e611ebe 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -104,7 +104,7 @@ namespace Umbraco.Tests.Scoping [Test] public void NestedMigrateScope() { - var scopeProvider = ScopeProvider as ScopeProvider; + var scopeProvider = ScopeProvider; Assert.IsNull(scopeProvider.AmbientScope); var httpContextItems = new Hashtable(); @@ -162,7 +162,7 @@ namespace Umbraco.Tests.Scoping Assert.AreSame(scope, scopeProvider.AmbientScope); Assert.IsNotNull(scopeProvider.AmbientContext); - ScopeContext context; + IScopeContext context; using (var nested = scopeProvider.CreateScope()) { Assert.IsInstanceOf(nested); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs index 1500e8a79d..c4a78ba9d5 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlCacheComponent.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // register the XML facade service composition.SetFacadeService(factory => new FacadeService( factory.GetInstance(), - (ScopeProvider) factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), factory.GetInstance().RequestCache, factory.GetInstance(),