Get rid of IScopeInternal, add IScopeContext
This commit is contained in:
42
src/Umbraco.Core/Scoping/IScopeContext.cs
Normal file
42
src/Umbraco.Core/Scoping/IScopeContext.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Umbraco.Core.Scoping
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a scope context.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>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.</remarks>
|
||||||
|
public interface IScopeContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enlists an action.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The action unique identifier.</param>
|
||||||
|
/// <param name="action">The action.</param>
|
||||||
|
/// <param name="priority">The optional action priority (default is 100, lower runs first).</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>It is ok to enlist multiple action with the same key but only the first one will run.</para>
|
||||||
|
/// <para>The action boolean parameter indicates whether the scope completed or not.</para>
|
||||||
|
/// </remarks>
|
||||||
|
void Enlist(string key, Action<bool> action, int priority = 100);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enlists an object and action.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the object.</typeparam>
|
||||||
|
/// <param name="key">The object unique identifier.</param>
|
||||||
|
/// <param name="creator">A function providing the object.</param>
|
||||||
|
/// <param name="action">The optional action.</param>
|
||||||
|
/// <param name="priority">The optional action priority (default is 100, lower runs first).</param>
|
||||||
|
/// <returns>The object.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>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.</para>
|
||||||
|
/// <para>The action boolean parameter indicates whether the scope completed or not.</para>
|
||||||
|
/// </remarks>
|
||||||
|
T Enlist<T>(string key, Func<T> creator, Action<bool, T> action = null, int priority = 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using System.Data;
|
|
||||||
using Umbraco.Core.Events;
|
|
||||||
using Umbraco.Core.Persistence;
|
|
||||||
|
|
||||||
namespace Umbraco.Core.Scoping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides additional, internal scope functionnalities.
|
|
||||||
/// </summary>
|
|
||||||
internal interface IScopeInternal : IScope // fixme - define what's internal and why
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the parent scope, if any, or null.
|
|
||||||
/// </summary>
|
|
||||||
IScopeInternal ParentScope { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether this scope should be registered in
|
|
||||||
/// call context even when an http context is available.
|
|
||||||
/// </summary>
|
|
||||||
bool CallContext { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the scope transaction isolation level.
|
|
||||||
/// </summary>
|
|
||||||
IsolationLevel IsolationLevel { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the scope database, if any, else null.
|
|
||||||
/// </summary>
|
|
||||||
IUmbracoDatabase DatabaseOrNull { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the scope event messages, if any, else null.
|
|
||||||
/// </summary>
|
|
||||||
EventMessages MessagesOrNull { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether filesystems are scoped.
|
|
||||||
/// </summary>
|
|
||||||
bool ScopedFileSystems { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers that a child has completed.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="completed">The child's completion status.</param>
|
|
||||||
/// <remarks>Completion status can be true (completed), false (could not complete), or null (not properly exited).</remarks>
|
|
||||||
void ChildCompleted(bool? completed);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resets the scope.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>Reset completion to "unspecified".</remarks>
|
|
||||||
void Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -74,7 +74,7 @@ namespace Umbraco.Core.Scoping
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the scope context.
|
/// Gets the scope context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ScopeContext Context { get; }
|
IScopeContext Context { get; }
|
||||||
|
|
||||||
#if DEBUG_SCOPES
|
#if DEBUG_SCOPES
|
||||||
Dictionary<Guid, object> CallContextObjects { get; }
|
Dictionary<Guid, object> CallContextObjects { get; }
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Scoping
|
|||||||
/// Implements <see cref="IScope"/>.
|
/// Implements <see cref="IScope"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Not thread-safe obviously.</remarks>
|
/// <remarks>Not thread-safe obviously.</remarks>
|
||||||
internal class Scope : IScopeInternal
|
internal class Scope : IScope
|
||||||
{
|
{
|
||||||
// fixme
|
// fixme
|
||||||
// considering that a great amount of things here are only useful for the top-level
|
// 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; }
|
public bool Detachable { get; }
|
||||||
|
|
||||||
// the parent scope (in a nested scopes chain)
|
// the parent scope (in a nested scopes chain)
|
||||||
public IScopeInternal ParentScope { get; set; }
|
public Scope ParentScope { get; set; }
|
||||||
|
|
||||||
public bool Attached { get; set; }
|
public bool Attached { get; set; }
|
||||||
|
|
||||||
// the original scope (when attaching a detachable scope)
|
// 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)
|
// the original context (when attaching a detachable scope)
|
||||||
public ScopeContext OrigContext { get; set; }
|
public ScopeContext OrigContext { get; set; }
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Umbraco.Core.Scoping
|
namespace Umbraco.Core.Scoping
|
||||||
{
|
{
|
||||||
// fixme should we have an IScopeContext?
|
internal class ScopeContext : IScopeContext, IInstanceIdentifiable
|
||||||
// fixme document all this properly!
|
|
||||||
|
|
||||||
public class ScopeContext : IInstanceIdentifiable
|
|
||||||
{
|
{
|
||||||
private Dictionary<string, IEnlistedObject> _enlisted;
|
private Dictionary<string, IEnlistedObject> _enlisted;
|
||||||
private bool _exiting;
|
private bool _exiting;
|
||||||
@@ -62,7 +59,7 @@ namespace Umbraco.Core.Scoping
|
|||||||
|
|
||||||
public T Item { get; }
|
public T Item { get; }
|
||||||
|
|
||||||
public int Priority { get; private set; }
|
public int Priority { get; }
|
||||||
|
|
||||||
public void Execute(bool completed)
|
public void Execute(bool completed)
|
||||||
{
|
{
|
||||||
@@ -70,38 +67,19 @@ namespace Umbraco.Core.Scoping
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: replace with optional parameters when we can break things
|
public void Enlist(string key, Action<bool> action, int priority = 100)
|
||||||
public T Enlist<T>(string key, Func<T> creator)
|
|
||||||
{
|
|
||||||
return Enlist(key, creator, null, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: replace with optional parameters when we can break things
|
|
||||||
public T Enlist<T>(string key, Func<T> creator, Action<bool, T> action)
|
|
||||||
{
|
|
||||||
return Enlist(key, creator, action, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: replace with optional parameters when we can break things
|
|
||||||
public void Enlist(string key, Action<bool> action)
|
|
||||||
{
|
|
||||||
Enlist<object>(key, null, (completed, item) => action(completed), 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Enlist(string key, Action<bool> action, int priority)
|
|
||||||
{
|
{
|
||||||
Enlist<object>(key, null, (completed, item) => action(completed), priority);
|
Enlist<object>(key, null, (completed, item) => action(completed), priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Enlist<T>(string key, Func<T> creator, Action<bool, T> action, int priority)
|
public T Enlist<T>(string key, Func<T> creator, Action<bool, T> action = null, int priority = 100)
|
||||||
{
|
{
|
||||||
if (_exiting)
|
if (_exiting)
|
||||||
throw new InvalidOperationException("Cannot enlist now, context is exiting.");
|
throw new InvalidOperationException("Cannot enlist now, context is exiting.");
|
||||||
|
|
||||||
var enlistedObjects = _enlisted ?? (_enlisted = new Dictionary<string, IEnlistedObject>());
|
var enlistedObjects = _enlisted ?? (_enlisted = new Dictionary<string, IEnlistedObject>());
|
||||||
|
|
||||||
IEnlistedObject enlisted;
|
if (enlistedObjects.TryGetValue(key, out IEnlistedObject enlisted))
|
||||||
if (enlistedObjects.TryGetValue(key, out enlisted))
|
|
||||||
{
|
{
|
||||||
var enlistedAs = enlisted as EnlistedObject<T>;
|
var enlistedAs = enlisted as EnlistedObject<T>;
|
||||||
if (enlistedAs == null) throw new InvalidOperationException("An item with the key already exists, but with a different type.");
|
if (enlistedAs == null) throw new InvalidOperationException("An item with the key already exists, but with a different type.");
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Collections;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Runtime.Remoting.Messaging;
|
using System.Runtime.Remoting.Messaging;
|
||||||
using System.Text;
|
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using Umbraco.Core.Composing;
|
using Umbraco.Core.Composing;
|
||||||
using Umbraco.Core.Events;
|
using Umbraco.Core.Events;
|
||||||
@@ -39,7 +38,7 @@ namespace Umbraco.Core.Scoping
|
|||||||
SafeCallContext.Register(
|
SafeCallContext.Register(
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
var scope = GetCallContextObject<IScopeInternal>(ScopeItemKey);
|
var scope = GetCallContextObject<Scope>(ScopeItemKey);
|
||||||
var context = GetCallContextObject<ScopeContext>(ContextItemKey);
|
var context = GetCallContextObject<ScopeContext>(ContextItemKey);
|
||||||
SetCallContextObject(ScopeItemKey, null);
|
SetCallContextObject(ScopeItemKey, null);
|
||||||
SetCallContextObject(ContextItemKey, null);
|
SetCallContextObject(ContextItemKey, null);
|
||||||
@@ -48,12 +47,12 @@ namespace Umbraco.Core.Scoping
|
|||||||
o =>
|
o =>
|
||||||
{
|
{
|
||||||
// cannot re-attached over leaked scope/context
|
// cannot re-attached over leaked scope/context
|
||||||
if (GetCallContextObject<IScope>(ScopeItemKey) != null)
|
if (GetCallContextObject<Scope>(ScopeItemKey) != null)
|
||||||
throw new Exception("Found leaked scope when restoring call context.");
|
throw new Exception("Found leaked scope when restoring call context.");
|
||||||
if (GetCallContextObject<ScopeContext>(ContextItemKey) != null)
|
if (GetCallContextObject<ScopeContext>(ContextItemKey) != null)
|
||||||
throw new Exception("Found leaked context when restoring call context.");
|
throw new Exception("Found leaked context when restoring call context.");
|
||||||
|
|
||||||
var t = (Tuple<IScopeInternal, ScopeContext>) o;
|
var t = (Tuple<Scope, ScopeContext>) o;
|
||||||
SetCallContextObject(ScopeItemKey, t.Item1);
|
SetCallContextObject(ScopeItemKey, t.Item1);
|
||||||
SetCallContextObject(ContextItemKey, t.Item2);
|
SetCallContextObject(ContextItemKey, t.Item2);
|
||||||
});
|
});
|
||||||
@@ -231,7 +230,7 @@ namespace Umbraco.Core.Scoping
|
|||||||
|
|
||||||
internal const string ContextItemKey = "Umbraco.Core.Scoping.ScopeContext";
|
internal const string ContextItemKey = "Umbraco.Core.Scoping.ScopeContext";
|
||||||
|
|
||||||
internal static ScopeContext AmbientContextInternal
|
internal static ScopeContext AmbientContextStatic
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -253,7 +252,7 @@ namespace Umbraco.Core.Scoping
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ScopeContext AmbientContext => AmbientContextInternal;
|
public ScopeContext AmbientContext => AmbientContextStatic;
|
||||||
|
|
||||||
#endregion
|
#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
|
// 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));
|
private static readonly ScopeReference StaticScopeReference = new ScopeReference(new ScopeProvider(null, null, null));
|
||||||
|
|
||||||
internal static IScopeInternal AmbientScopeInternal
|
private static Scope AmbientScopeStatic
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// try http context, fallback onto call context
|
// try http context, fallback onto call context
|
||||||
var value = GetHttpContextObject<IScopeInternal>(ScopeItemKey, false);
|
var value = GetHttpContextObject<Scope>(ScopeItemKey, false);
|
||||||
return value ?? GetCallContextObject<IScopeInternal>(ScopeItemKey);
|
return value ?? GetCallContextObject<Scope>(ScopeItemKey);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
@@ -291,15 +290,15 @@ namespace Umbraco.Core.Scoping
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IScopeInternal AmbientScope
|
public Scope AmbientScope
|
||||||
{
|
{
|
||||||
get => AmbientScopeInternal;
|
get => AmbientScopeStatic;
|
||||||
internal set => AmbientScopeInternal = value;
|
set => AmbientScopeStatic = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void SetAmbient(IScopeInternal scope, ScopeContext context = null)
|
public void SetAmbient(Scope scope, ScopeContext context = null)
|
||||||
{
|
{
|
||||||
// clear all
|
// clear all
|
||||||
SetHttpContextObject(ScopeItemKey, null, false);
|
SetHttpContextObject(ScopeItemKey, null, false);
|
||||||
@@ -415,7 +414,7 @@ namespace Umbraco.Core.Scoping
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ScopeContext Context => AmbientContext;
|
public IScopeContext Context => AmbientContext;
|
||||||
|
|
||||||
#if DEBUG_SCOPES
|
#if DEBUG_SCOPES
|
||||||
// this code needs TLC
|
// this code needs TLC
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
{
|
{
|
||||||
// dispose the entire chain (if any)
|
// dispose the entire chain (if any)
|
||||||
// reset (don't commit by default)
|
// reset (don't commit by default)
|
||||||
IScopeInternal scope;
|
Scope scope;
|
||||||
while ((scope = _scopeProvider.AmbientScope) != null)
|
while ((scope = _scopeProvider.AmbientScope) != null)
|
||||||
{
|
{
|
||||||
scope.Reset();
|
scope.Reset();
|
||||||
|
|||||||
@@ -1253,7 +1253,7 @@
|
|||||||
<Compile Include="SafeCallContext.cs" />
|
<Compile Include="SafeCallContext.cs" />
|
||||||
<Compile Include="Scoping\IInstanceIdentifiable.cs" />
|
<Compile Include="Scoping\IInstanceIdentifiable.cs" />
|
||||||
<Compile Include="Scoping\IScope.cs" />
|
<Compile Include="Scoping\IScope.cs" />
|
||||||
<Compile Include="Scoping\IScopeInternal.cs" />
|
<Compile Include="Scoping\IScopeContext.cs" />
|
||||||
<Compile Include="Scoping\IScopeProvider.cs" />
|
<Compile Include="Scoping\IScopeProvider.cs" />
|
||||||
<Compile Include="Scoping\RepositoryCacheMode.cs" />
|
<Compile Include="Scoping\RepositoryCacheMode.cs" />
|
||||||
<Compile Include="Scoping\Scope.cs" />
|
<Compile Include="Scoping\Scope.cs" />
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ namespace Umbraco.Tests.Scoping
|
|||||||
[Test]
|
[Test]
|
||||||
public void NestedMigrateScope()
|
public void NestedMigrateScope()
|
||||||
{
|
{
|
||||||
var scopeProvider = ScopeProvider as ScopeProvider;
|
var scopeProvider = ScopeProvider;
|
||||||
Assert.IsNull(scopeProvider.AmbientScope);
|
Assert.IsNull(scopeProvider.AmbientScope);
|
||||||
|
|
||||||
var httpContextItems = new Hashtable();
|
var httpContextItems = new Hashtable();
|
||||||
@@ -162,7 +162,7 @@ namespace Umbraco.Tests.Scoping
|
|||||||
Assert.AreSame(scope, scopeProvider.AmbientScope);
|
Assert.AreSame(scope, scopeProvider.AmbientScope);
|
||||||
Assert.IsNotNull(scopeProvider.AmbientContext);
|
Assert.IsNotNull(scopeProvider.AmbientContext);
|
||||||
|
|
||||||
ScopeContext context;
|
IScopeContext context;
|
||||||
using (var nested = scopeProvider.CreateScope())
|
using (var nested = scopeProvider.CreateScope())
|
||||||
{
|
{
|
||||||
Assert.IsInstanceOf<Scope>(nested);
|
Assert.IsInstanceOf<Scope>(nested);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
|||||||
// register the XML facade service
|
// register the XML facade service
|
||||||
composition.SetFacadeService(factory => new FacadeService(
|
composition.SetFacadeService(factory => new FacadeService(
|
||||||
factory.GetInstance<ServiceContext>(),
|
factory.GetInstance<ServiceContext>(),
|
||||||
(ScopeProvider) factory.GetInstance<IScopeProvider>(),
|
factory.GetInstance<IScopeProvider>(),
|
||||||
factory.GetInstance<IScopeUnitOfWorkProvider>(),
|
factory.GetInstance<IScopeUnitOfWorkProvider>(),
|
||||||
factory.GetInstance<CacheHelper>().RequestCache,
|
factory.GetInstance<CacheHelper>().RequestCache,
|
||||||
factory.GetInstance<UrlSegmentProviderCollection>(),
|
factory.GetInstance<UrlSegmentProviderCollection>(),
|
||||||
|
|||||||
Reference in New Issue
Block a user