U4-9422 - complete implementing scoped xml cache

This commit is contained in:
Stephan
2017-01-24 07:59:46 +01:00
parent 26a95a8ca1
commit a97b566065
7 changed files with 110 additions and 79 deletions

View File

@@ -30,8 +30,9 @@ namespace Umbraco.Core.Cache
private void RegisterDirty()
{
// use unique names to de-duplicate
_scope.OnExit("dirty_" + typeof (TEntity).Name,
() => _globalIsolatedCache.ClearAllCache());
// enlisting multiple times is not a problem
_scope.Enlist("dirty_" + typeof (TEntity).Name, ActionTime.BeforeDispose,
(actionTime, completed) => { if (completed) _globalIsolatedCache.ClearAllCache(); });
}
public TEntity Get(TId id, Func<TId, TEntity> performGet, Func<TId[], IEnumerable<TEntity>> performGetAll)

View File

@@ -1,9 +1,13 @@
namespace Umbraco.Core.Scoping
using System;
namespace Umbraco.Core.Scoping
{
[Flags]
public enum ActionTime
{
BeforeCommit,
BeforeEvent,
BeforeDispose
None = 0,
BeforeCommit = 1,
BeforeEvent = 2,
BeforeDispose = 4
}
}

View File

@@ -36,25 +36,50 @@ namespace Umbraco.Core.Scoping
/// </summary>
void Complete();
///// <summary>
///// Registers an action to execute on exit.
///// </summary>
///// <param name="key">The unique key of the action.</param>
///// <param name="action">The action.</param>
///// <remarks>
///// <para>The key is unique (as in, dictionary key).</para>
///// <para>The action will execute only if the scope completes.</para>
///// </remarks>
//void OnExit(string key, Action action);
///// <remarks>
///// <para>The key is unique (as in, dictionary key).</para>
///// <para>The action always executes, with an argument indicating whether the scope completed.</para>
///// </remarks>
//void OnExit(string key, Action<bool> action);
/// <summary>
/// Registers an action to execute on exit.
/// Enlists an object into the scope.
/// </summary>
/// <param name="key">The unique key of the action.</param>
/// <param name="action">The action.</param>
/// <remarks>
/// <para>The key is unique (as in, dictionary key).</para>
/// <para>The action will execute only if the scope completes.</para>
/// </remarks>
void OnExit(string key, Action action);
/// <typeparam name="T">The type of the object.</typeparam>
/// <param name="key">The unique key.</param>
/// <param name="creator">A method creating the object.</param>
/// <returns>The object.</returns>
T Enlist<T>(string key, Func<T> creator);
/// <remarks>
/// <para>The key is unique (as in, dictionary key).</para>
/// <para>The action always executes, with an argument indicating whether the scope completed.</para>
/// </remarks>
void OnExit(string key, Action<bool> action);
/// <summary>
/// Enlists an action into the scope.
/// </summary>
/// <param name="key">The unique key.</param>
/// <param name="actionTimes">When to execute the action.</param>
/// <param name="action">The action to execute.</param>
void Enlist(string key, ActionTime actionTimes, Action<ActionTime, bool> action);
// fixme
T Enlist<T>(string key, Func<T> creator, Action<ActionTime, bool, T> action);
/// <summary>
/// Enlists an object and an action into the scope.
/// </summary>
/// <typeparam name="T">The type of the object.</typeparam>
/// <param name="key">The unique key.</param>
/// <param name="creator">A method creating the object.</param>
/// <param name="actionTimes">When to execute the action.</param>
/// <param name="action">The action to execute.</param>
/// <returns>The object.</returns>
T Enlist<T>(string key, Func<T> creator, ActionTime actionTimes, Action<ActionTime, bool, T> action);
#if DEBUG_SCOPES
Guid InstanceId { get; }

View File

@@ -84,19 +84,19 @@ namespace Umbraco.Core.Scoping
}
/// <inheritdoc />
public void OnExit(string key, Action action)
public T Enlist<T>(string key, Func<T> creator)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public void OnExit(string key, Action<bool> action)
public T Enlist<T>(string key, Func<T> creator, ActionTime actionTimes, Action<ActionTime, bool, T> action)
{
throw new NotImplementedException();
}
// fixme
public T Enlist<T>(string key, Func<T> creator, Action<ActionTime, bool, T> action)
/// <inheritdoc />
public void Enlist(string key, ActionTime actionTimes, Action<ActionTime, bool> action)
{
throw new NotImplementedException();
}

View File

@@ -22,7 +22,7 @@ namespace Umbraco.Core.Scoping
private IsolatedRuntimeCache _isolatedRuntimeCache;
private UmbracoDatabase _database;
private IList<EventMessage> _messages;
private IDictionary<string, Action<bool>> _exitActions;
private IDictionary<string, IEnlistedObject> _enlisted;
// this is v7, in v8 this has to change to RepeatableRead
private const IsolationLevel DefaultIsolationLevel = IsolationLevel.ReadCommitted;
@@ -258,31 +258,18 @@ namespace Umbraco.Core.Scoping
}
}
// run everything we need to run when completing
List<Exception> exceptions = null;
foreach (var action in ExitActions.Values)
{
try
{
action(completed); // fixme try catch and everything
}
catch (Exception e)
{
if (exceptions == null)
exceptions = new List<Exception>();
exceptions.Add(e);
}
}
if (exceptions != null)
throw new AggregateException("Exceptions were throws by complete actions.", exceptions);
// run enlisted actions
exceptions = null;
RunEnlisted(ActionTime.BeforeDispose, completed);
}
private void RunEnlisted(ActionTime actionTime, bool completed)
{
List<Exception> exceptions = null;
foreach (var enlisted in Enlisted.Values)
{
try
{
enlisted.Execute(ActionTime.BeforeDispose, completed);
enlisted.Execute(actionTime, completed);
}
catch (Exception e)
{
@@ -292,34 +279,9 @@ namespace Umbraco.Core.Scoping
}
}
if (exceptions != null)
throw new AggregateException("Exceptions were thrown by listed actions at ActionTime.BeforeDispose.", exceptions);
throw new AggregateException("Exceptions were thrown by listed actions at ActionTime " + actionTime + ".", exceptions);
}
private IDictionary<string, Action<bool>> ExitActions
{
get
{
if (ParentScope != null) return ParentScope.ExitActions;
return _exitActions ?? (_exitActions
= new Dictionary<string, Action<bool>>());
}
}
/// <inheritdoc />
public void OnExit(string key, Action action)
{
ExitActions[key] = completed => { if (completed) action(); };
}
/// <inheritdoc />
public void OnExit(string key, Action<bool> action)
{
ExitActions[key] = action;
}
private IDictionary<string, IEnlistedObject> _enlisted;
private IDictionary<string, IEnlistedObject> Enlisted
{
get
@@ -338,11 +300,19 @@ namespace Umbraco.Core.Scoping
private class EnlistedObject<T> : IEnlistedObject
{
private readonly ActionTime _actionTimes;
private readonly Action<ActionTime, bool, T> _action;
public EnlistedObject(T item, Action<ActionTime, bool, T> action)
public EnlistedObject(T item)
{
Item = item;
_actionTimes = ActionTime.None;
}
public EnlistedObject(T item, ActionTime actionTimes, Action<ActionTime, bool, T> action)
{
Item = item;
_actionTimes = actionTimes;
_action = action;
}
@@ -350,11 +320,13 @@ namespace Umbraco.Core.Scoping
public void Execute(ActionTime actionTime, bool completed)
{
_action(actionTime, completed, Item);
if (_actionTimes.HasFlag(actionTime))
_action(actionTime, completed, Item);
}
}
public T Enlist<T>(string key, Func<T> creator, Action<ActionTime, bool, T> action)
/// <inheritdoc />
public T Enlist<T>(string key, Func<T> creator)
{
IEnlistedObject enlisted;
if (Enlisted.TryGetValue(key, out enlisted))
@@ -363,9 +335,38 @@ namespace Umbraco.Core.Scoping
if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key.");
return enlistedAs.Item;
}
var enlistedOfT = new EnlistedObject<T>(creator(), action);
var enlistedOfT = new EnlistedObject<T>(creator());
Enlisted[key] = enlistedOfT;
return enlistedOfT.Item;
}
/// <inheritdoc />
public T Enlist<T>(string key, Func<T> creator, ActionTime actionTimes, Action<ActionTime, bool, T> action)
{
IEnlistedObject enlisted;
if (Enlisted.TryGetValue(key, out enlisted))
{
var enlistedAs = enlisted as EnlistedObject<T>;
if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key.");
return enlistedAs.Item;
}
var enlistedOfT = new EnlistedObject<T>(creator(), actionTimes, action);
Enlisted[key] = enlistedOfT;
return enlistedOfT.Item;
}
/// <inheritdoc />
public void Enlist(string key, ActionTime actionTimes, Action<ActionTime, bool> action)
{
IEnlistedObject enlisted;
if (Enlisted.TryGetValue(key, out enlisted))
{
var enlistedAs = enlisted as EnlistedObject<object>;
if (enlistedAs == null) throw new Exception("An item with a different type has already been enlisted with the same key.");
return;
}
var enlistedOfT = new EnlistedObject<object>(null, actionTimes, (actionTime, completed, item) => action(actionTime, completed));
Enlisted[key] = enlistedOfT;
}
}
}

View File

@@ -421,7 +421,7 @@ namespace Umbraco.Tests.Scoping
[TestCase(true)]
[TestCase(false)]
public void ScopeAction(bool complete)
public void ScopeEnlist(bool complete)
{
var scopeProvider = DatabaseContext.ScopeProvider;
@@ -430,7 +430,7 @@ namespace Umbraco.Tests.Scoping
Assert.IsNull(scopeProvider.AmbientScope);
using (var scope = scopeProvider.CreateScope())
{
((Scope) scope).OnExit("name", x => completed = x);
scope.Enlist("name", ActionTime.BeforeDispose, (a, c) => { completed = c; });
if (complete)
scope.Complete();
}

View File

@@ -759,9 +759,9 @@ namespace umbraco
var releaser = instance._xmlLock.Lock();
return new SafeXmlReaderWriter(instance, releaser, writer, true);
},
ActionTime.BeforeDispose,
(actionTime, completed, item) => // action
{
if (actionTime != ActionTime.BeforeDispose) return;
item.DisposeForReal(completed);
});