Merge branch 'temp8' into temp8-3668-query-builder-snippits
This commit is contained in:
+4
-1
@@ -34,4 +34,7 @@ dotnet_naming_style.prefix_underscore.required_prefix = _
|
||||
csharp_style_var_for_built_in_types = true:suggestion
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
csharp_prefer_braces = false : none
|
||||
csharp_prefer_braces = false : none
|
||||
|
||||
[*.{js,less}]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
@@ -104,7 +104,6 @@ There's two big areas that you should know about:
|
||||
You may need to run the following commands to set up gulp properly:
|
||||
```
|
||||
npm cache clean
|
||||
npm install -g bower
|
||||
npm install -g gulp
|
||||
npm install -g gulp-cli
|
||||
npm install
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
|
||||
[](https://pullreminders.com?ref=badge)
|
||||
|
||||
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS) to go to the v7 branch._
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
* Open the `/src/umbraco.sln` Visual Studio solution
|
||||
* Start the solution (easiest way is to use `ctrl + F5`)
|
||||
* When the solution is first built this may take some time since it will restore all nuget, npm and bower packages, build the .net solution and also build the angular solution
|
||||
* When the solution is first built this may take some time since it will restore all nuget and npm packages, build the .net solution and also build the angular solution
|
||||
* When the website starts you'll see the Umbraco installer and just follow the prompts
|
||||
* You're all set!
|
||||
|
||||
|
||||
@@ -52,10 +52,8 @@
|
||||
<!-- config transforms -->
|
||||
<!-- beware! config transforms not supported by PackageReference -->
|
||||
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
|
||||
<file src="tools\applications.config.install.xdt" target="Content\config\applications.config.install.xdt" />
|
||||
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.config.install.xdt" />
|
||||
<file src="tools\Dashboard.config.install.xdt" target="Content\config\Dashboard.config.install.xdt" />
|
||||
<file src="tools\trees.config.install.xdt" target="Content\config\trees.config.install.xdt" />
|
||||
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
|
||||
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" /> <!-- FIXME Content\ !! and then... transform?! -->
|
||||
|
||||
|
||||
@@ -330,9 +330,6 @@
|
||||
|
||||
$ubuild.DefineMethod("PrepareBuild",
|
||||
{
|
||||
Write-Host "Clear folders and files"
|
||||
$this.RemoveDirectory("$($this.SolutionRoot)\src\Umbraco.Web.UI.Client\bower_components")
|
||||
|
||||
$this.TempStoreFile("$($this.SolutionRoot)\src\Umbraco.Web.UI\web.config")
|
||||
Write-Host "Create clean web.config"
|
||||
$this.CopyFile("$($this.SolutionRoot)\src\Umbraco.Web.UI\web.Template.config", "$($this.SolutionRoot)\src\Umbraco.Web.UI\web.config")
|
||||
|
||||
+13
-13
@@ -8,9 +8,9 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Extensions for strongly typed access
|
||||
/// </summary>
|
||||
public static class CacheProviderExtensions
|
||||
public static class AppCacheExtensions
|
||||
{
|
||||
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
|
||||
public static T GetCacheItem<T>(this IAppPolicyCache provider,
|
||||
string cacheKey,
|
||||
Func<T> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
@@ -19,11 +19,11 @@ namespace Umbraco.Core.Cache
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
var result = provider.Get(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
return result == null ? default(T) : result.TryConvertTo<T>().Result;
|
||||
}
|
||||
|
||||
public static void InsertCacheItem<T>(this IRuntimeCacheProvider provider,
|
||||
public static void InsertCacheItem<T>(this IAppPolicyCache provider,
|
||||
string cacheKey,
|
||||
Func<T> getCacheItem,
|
||||
TimeSpan? timeout = null,
|
||||
@@ -32,24 +32,24 @@ namespace Umbraco.Core.Cache
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null)
|
||||
{
|
||||
provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
provider.Insert(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this ICacheProvider provider, string keyStartsWith)
|
||||
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this IAppCache provider, string keyStartsWith)
|
||||
{
|
||||
var result = provider.GetCacheItemsByKeySearch(keyStartsWith);
|
||||
var result = provider.SearchByKey(keyStartsWith);
|
||||
return result.Select(x => x.TryConvertTo<T>().Result);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
|
||||
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this IAppCache provider, string regexString)
|
||||
{
|
||||
var result = provider.GetCacheItemsByKeyExpression(regexString);
|
||||
var result = provider.SearchByRegex(regexString);
|
||||
return result.Select(x => x.TryConvertTo<T>().Result);
|
||||
}
|
||||
|
||||
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
|
||||
public static T GetCacheItem<T>(this IAppCache provider, string cacheKey)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey);
|
||||
var result = provider.Get(cacheKey);
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
@@ -57,9 +57,9 @@ namespace Umbraco.Core.Cache
|
||||
return result.TryConvertTo<T>().Result;
|
||||
}
|
||||
|
||||
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey, Func<T> getCacheItem)
|
||||
public static T GetCacheItem<T>(this IAppCache provider, string cacheKey, Func<T> getCacheItem)
|
||||
{
|
||||
var result = provider.GetCacheItem(cacheKey, () => getCacheItem());
|
||||
var result = provider.Get(cacheKey, () => getCacheItem());
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the application caches.
|
||||
/// </summary>
|
||||
public class AppCaches
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppCaches"/> for use in a web application.
|
||||
/// </summary>
|
||||
public AppCaches()
|
||||
: this(HttpRuntime.Cache)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppCaches"/> for use in a web application.
|
||||
/// </summary>
|
||||
public AppCaches(System.Web.Caching.Cache cache)
|
||||
: this(
|
||||
new WebCachingAppCache(cache),
|
||||
new HttpRequestAppCache(),
|
||||
new IsolatedCaches(t => new ObjectCacheAppCache()))
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppCaches"/> with cache providers.
|
||||
/// </summary>
|
||||
public AppCaches(
|
||||
IAppPolicyCache runtimeCache,
|
||||
IAppCache requestCache,
|
||||
IsolatedCaches isolatedCaches)
|
||||
{
|
||||
RuntimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
|
||||
RequestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
|
||||
IsolatedCaches = isolatedCaches ?? throw new ArgumentNullException(nameof(isolatedCaches));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the special disabled instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When used by repositories, all cache policies apply, but the underlying caches do not cache anything.</para>
|
||||
/// <para>Used by tests.</para>
|
||||
/// </remarks>
|
||||
public static AppCaches Disabled { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(_ => NoAppCache.Instance));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the special no-cache instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When used by repositories, all cache policies are bypassed.</para>
|
||||
/// <para>Used by repositories that do no cache.</para>
|
||||
/// </remarks>
|
||||
public static AppCaches NoCache { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(_ => NoAppCache.Instance));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the per-request cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The per-request caches works on top of the current HttpContext items.</para>
|
||||
/// fixme - what about non-web applications?
|
||||
/// </remarks>
|
||||
public IAppCache RequestCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the runtime cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>The runtime cache is the main application cache.</para>
|
||||
/// </remarks>
|
||||
public IAppPolicyCache RuntimeCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the isolated caches.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Isolated caches are used by e.g. repositories, to ensure that each cached entity
|
||||
/// type has its own cache, so that lookups are fast and the repository does not need to
|
||||
/// search through all keys on a global scale.</para>
|
||||
/// </remarks>
|
||||
public IsolatedCaches IsolatedCaches { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for implementing a dictionary of <see cref="IAppPolicyCache"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the dictionary key.</typeparam>
|
||||
public abstract class AppPolicedCacheDictionary<TKey>
|
||||
{
|
||||
private readonly ConcurrentDictionary<TKey, IAppPolicyCache> _caches = new ConcurrentDictionary<TKey, IAppPolicyCache>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppPolicedCacheDictionary{TKey}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="cacheFactory"></param>
|
||||
protected AppPolicedCacheDictionary(Func<TKey, IAppPolicyCache> cacheFactory)
|
||||
{
|
||||
CacheFactory = cacheFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal cache factory, for tests only!
|
||||
/// </summary>
|
||||
internal readonly Func<TKey, IAppPolicyCache> CacheFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a cache.
|
||||
/// </summary>
|
||||
public IAppPolicyCache GetOrCreate(TKey key)
|
||||
=> _caches.GetOrAdd(key, k => CacheFactory(k));
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a cache.
|
||||
/// </summary>
|
||||
public Attempt<IAppPolicyCache> Get(TKey key)
|
||||
=> _caches.TryGetValue(key, out var cache) ? Attempt.Succeed(cache) : Attempt.Fail<IAppPolicyCache>();
|
||||
|
||||
/// <summary>
|
||||
/// Removes a cache.
|
||||
/// </summary>
|
||||
public void Remove(TKey key)
|
||||
{
|
||||
_caches.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all caches.
|
||||
/// </summary>
|
||||
public void RemoveAll()
|
||||
{
|
||||
_caches.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears a cache.
|
||||
/// </summary>
|
||||
public void ClearCache(TKey key)
|
||||
{
|
||||
if (_caches.TryGetValue(key, out var cache))
|
||||
cache.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all caches.
|
||||
/// </summary>
|
||||
public void ClearAllCaches()
|
||||
{
|
||||
foreach (var cache in _caches.Values)
|
||||
cache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the application-wide caches.
|
||||
/// </summary>
|
||||
public class CacheHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance for use in the web
|
||||
/// </summary>
|
||||
public CacheHelper()
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(),
|
||||
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance for use in the web
|
||||
/// </summary>
|
||||
public CacheHelper(System.Web.Caching.Cache cache)
|
||||
: this(
|
||||
new HttpRuntimeCacheProvider(cache),
|
||||
new StaticCacheProvider(),
|
||||
new HttpRequestCacheProvider(),
|
||||
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance based on the provided providers
|
||||
/// </summary>
|
||||
public CacheHelper(
|
||||
IRuntimeCacheProvider httpCacheProvider,
|
||||
ICacheProvider staticCacheProvider,
|
||||
ICacheProvider requestCacheProvider,
|
||||
IsolatedRuntimeCache isolatedCacheManager)
|
||||
{
|
||||
RuntimeCache = httpCacheProvider ?? throw new ArgumentNullException(nameof(httpCacheProvider));
|
||||
StaticCache = staticCacheProvider ?? throw new ArgumentNullException(nameof(staticCacheProvider));
|
||||
RequestCache = requestCacheProvider ?? throw new ArgumentNullException(nameof(requestCacheProvider));
|
||||
IsolatedRuntimeCache = isolatedCacheManager ?? throw new ArgumentNullException(nameof(isolatedCacheManager));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the special disabled instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When used by repositories, all cache policies apply, but the underlying caches do not cache anything.</para>
|
||||
/// <para>Used by tests.</para>
|
||||
/// </remarks>
|
||||
public static CacheHelper Disabled { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the special no-cache instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When used by repositories, all cache policies are bypassed.</para>
|
||||
/// <para>Used by repositories that do no cache.</para>
|
||||
/// </remarks>
|
||||
public static CacheHelper NoCache { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Request cache
|
||||
/// </summary>
|
||||
public ICacheProvider RequestCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public ICacheProvider StaticCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Runtime cache
|
||||
/// </summary>
|
||||
public IRuntimeCacheProvider RuntimeCache { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current Isolated Runtime cache manager
|
||||
/// </summary>
|
||||
public IsolatedRuntimeCache IsolatedRuntimeCache { get; internal set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,10 +16,10 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CacheRefresherBase{TInstanceType}"/>.
|
||||
/// </summary>
|
||||
/// <param name="cacheHelper">A cache helper.</param>
|
||||
protected CacheRefresherBase(CacheHelper cacheHelper)
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected CacheRefresherBase(AppCaches appCaches)
|
||||
{
|
||||
CacheHelper = cacheHelper;
|
||||
AppCaches = appCaches;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -93,7 +93,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Gets the cache helper.
|
||||
/// </summary>
|
||||
protected CacheHelper CacheHelper { get; }
|
||||
protected AppCaches AppCaches { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cache for all repository entities of a specified type.
|
||||
@@ -102,7 +102,7 @@ namespace Umbraco.Core.Cache
|
||||
protected void ClearAllIsolatedCacheByEntityType<TEntity>()
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
CacheHelper.IsolatedRuntimeCache.ClearCache<TEntity>();
|
||||
AppCaches.IsolatedCaches.ClearCache<TEntity>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IAppPolicyCache"/> by wrapping an inner other <see cref="IAppPolicyCache"/>
|
||||
/// instance, and ensuring that all inserts and returns are deep cloned copies of the cache item,
|
||||
/// when the item is deep-cloneable.
|
||||
/// </summary>
|
||||
internal class DeepCloneAppCache : IAppPolicyCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeepCloneAppCache"/> class.
|
||||
/// </summary>
|
||||
public DeepCloneAppCache(IAppPolicyCache innerCache)
|
||||
{
|
||||
var type = typeof (DeepCloneAppCache);
|
||||
|
||||
if (innerCache.GetType() == type)
|
||||
throw new InvalidOperationException($"A {type} cannot wrap another instance of itself.");
|
||||
|
||||
InnerCache = innerCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the inner cache.
|
||||
/// </summary>
|
||||
public IAppPolicyCache InnerCache { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key)
|
||||
{
|
||||
var item = InnerCache.Get(key);
|
||||
return CheckCloneableAndTracksChanges(item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory)
|
||||
{
|
||||
var cached = InnerCache.Get(key, () =>
|
||||
{
|
||||
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
// do not store null values (backward compat), clone / reset to go into the cache
|
||||
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
||||
});
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
return InnerCache.SearchByKey(keyStartsWith)
|
||||
.Select(CheckCloneableAndTracksChanges);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
return InnerCache.SearchByRegex(regex)
|
||||
.Select(CheckCloneableAndTracksChanges);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var cached = InnerCache.Get(key, () =>
|
||||
{
|
||||
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
// do not store null values (backward compat), clone / reset to go into the cache
|
||||
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
||||
|
||||
// clone / reset to go into the cache
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
|
||||
// clone / reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
InnerCache.Insert(key, () =>
|
||||
{
|
||||
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
// do not store null values (backward compat), clone / reset to go into the cache
|
||||
return value == null ? null : CheckCloneableAndTracksChanges(value);
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
{
|
||||
InnerCache.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear(string key)
|
||||
{
|
||||
InnerCache.Clear(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType(string typeName)
|
||||
{
|
||||
InnerCache.ClearOfType(typeName);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType<T>()
|
||||
{
|
||||
InnerCache.ClearOfType<T>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
InnerCache.ClearOfType<T>(predicate);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
InnerCache.ClearByKey(keyStartsWith);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearByRegex(string regex)
|
||||
{
|
||||
InnerCache.ClearByRegex(regex);
|
||||
}
|
||||
|
||||
private static object CheckCloneableAndTracksChanges(object input)
|
||||
{
|
||||
if (input is IDeepCloneable cloneable)
|
||||
{
|
||||
input = cloneable.DeepClone();
|
||||
}
|
||||
|
||||
// reset dirty initial properties
|
||||
if (input is IRememberBeingDirty tracksChanges)
|
||||
{
|
||||
tracksChanges.ResetDirtyProperties(false);
|
||||
input = tracksChanges;
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface describing this cache provider as a wrapper for another
|
||||
/// </summary>
|
||||
internal interface IRuntimeCacheProviderWrapper
|
||||
{
|
||||
IRuntimeCacheProvider InnerProvider { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for any IRuntimeCacheProvider that ensures that all inserts and returns
|
||||
/// are a deep cloned copy of the item when the item is IDeepCloneable and that tracks changes are
|
||||
/// reset if the object is TracksChangesEntityBase
|
||||
/// </summary>
|
||||
internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider, IRuntimeCacheProviderWrapper
|
||||
{
|
||||
public IRuntimeCacheProvider InnerProvider { get; }
|
||||
|
||||
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
|
||||
{
|
||||
var type = typeof (DeepCloneRuntimeCacheProvider);
|
||||
|
||||
if (innerProvider.GetType() == type)
|
||||
throw new InvalidOperationException($"A {type} cannot wrap another instance of {type}.");
|
||||
|
||||
InnerProvider = innerProvider;
|
||||
}
|
||||
|
||||
#region Clear - doesn't require any changes
|
||||
|
||||
public void ClearAllCache()
|
||||
{
|
||||
InnerProvider.ClearAllCache();
|
||||
}
|
||||
|
||||
public void ClearCacheItem(string key)
|
||||
{
|
||||
InnerProvider.ClearCacheItem(key);
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
InnerProvider.ClearCacheObjectTypes(typeName);
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
InnerProvider.ClearCacheObjectTypes<T>();
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
InnerProvider.ClearCacheObjectTypes<T>(predicate);
|
||||
}
|
||||
|
||||
public void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
InnerProvider.ClearCacheByKeySearch(keyStartsWith);
|
||||
}
|
||||
|
||||
public void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
InnerProvider.ClearCacheByKeyExpression(regexString);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return InnerProvider.GetCacheItemsByKeySearch(keyStartsWith)
|
||||
.Select(CheckCloneableAndTracksChanges);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return InnerProvider.GetCacheItemsByKeyExpression(regexString)
|
||||
.Select(CheckCloneableAndTracksChanges);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var item = InnerProvider.GetCacheItem(cacheKey);
|
||||
return CheckCloneableAndTracksChanges(item);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
var cached = InnerProvider.GetCacheItem(cacheKey, () =>
|
||||
{
|
||||
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return null; // do not store null values (backward compat)
|
||||
|
||||
return CheckCloneableAndTracksChanges(value);
|
||||
});
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var cached = InnerProvider.GetCacheItem(cacheKey, () =>
|
||||
{
|
||||
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return null; // do not store null values (backward compat)
|
||||
|
||||
// clone / reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(value);
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
|
||||
// clone / reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
InnerProvider.InsertCacheItem(cacheKey, () =>
|
||||
{
|
||||
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return null; // do not store null values (backward compat)
|
||||
|
||||
// clone / reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(value);
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
private static object CheckCloneableAndTracksChanges(object input)
|
||||
{
|
||||
var cloneable = input as IDeepCloneable;
|
||||
if (cloneable != null)
|
||||
{
|
||||
input = cloneable.DeepClone();
|
||||
}
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
var tracksChanges = input as IRememberBeingDirty;
|
||||
if (tracksChanges != null)
|
||||
{
|
||||
tracksChanges.ResetDirtyProperties(false);
|
||||
input = tracksChanges;
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Cache
|
||||
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
public DefaultRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
: base(cache, scopeAccessor)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
protected virtual void InsertEntity(string cacheKey, TEntity entity)
|
||||
{
|
||||
Cache.InsertCacheItem(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
protected virtual void InsertEntities(TId[] ids, TEntity[] entities)
|
||||
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Cache
|
||||
// getting all of them, and finding nothing.
|
||||
// if we can cache a zero count, cache an empty array,
|
||||
// for as long as the cache is not cleared (no expiration)
|
||||
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => EmptyEntities);
|
||||
Cache.Insert(GetEntityTypeCacheKey(), () => EmptyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Cache
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var capture = entity;
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,21 +77,21 @@ namespace Umbraco.Core.Cache
|
||||
// just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if an exception is thrown we need to remove the entry from cache,
|
||||
// this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
|
||||
Cache.Clear(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -109,21 +109,21 @@ namespace Umbraco.Core.Cache
|
||||
// just to be safe, we cannot cache an item without an identity
|
||||
if (entity.HasIdentity)
|
||||
{
|
||||
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if an exception is thrown we need to remove the entry from cache,
|
||||
// this is ONLY a work around because of the way
|
||||
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
|
||||
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
|
||||
Cache.Clear(GetEntityCacheKey(entity.Id));
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
|
||||
throw;
|
||||
}
|
||||
@@ -142,9 +142,9 @@ namespace Umbraco.Core.Cache
|
||||
{
|
||||
// whatever happens, clear the cache
|
||||
var cacheKey = GetEntityCacheKey(entity.Id);
|
||||
Cache.ClearCacheItem(cacheKey);
|
||||
Cache.Clear(cacheKey);
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey());
|
||||
Cache.ClearByKey(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IAppCache"/> on top of a concurrent dictionary.
|
||||
/// </summary>
|
||||
public class DictionaryAppCache : IAppCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the internal items dictionary, for tests only!
|
||||
/// </summary>
|
||||
internal readonly ConcurrentDictionary<string, object> Items = new ConcurrentDictionary<string, object>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string key)
|
||||
{
|
||||
// fixme throws if non-existing, shouldn't it return null?
|
||||
return Items[key];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string key, Func<object> factory)
|
||||
{
|
||||
return Items.GetOrAdd(key, _ => factory());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
var items = new List<object>();
|
||||
foreach (var (key, value) in Items)
|
||||
if (key.InvariantStartsWith(keyStartsWith))
|
||||
items.Add(value);
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
var items = new List<object>();
|
||||
foreach (var (key, value) in Items)
|
||||
if (compiled.IsMatch(key))
|
||||
items.Add(value);
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear()
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear(string key)
|
||||
{
|
||||
Items.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType(string typeName)
|
||||
{
|
||||
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
Items.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
Items.RemoveAll(kvp => compiled.IsMatch(kvp.Key));
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
-68
@@ -7,79 +7,130 @@ using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal class DictionaryCacheProvider : ICacheProvider
|
||||
/// <summary>
|
||||
/// Implements a fast <see cref="IAppCache"/> on top of a concurrent dictionary.
|
||||
/// </summary>
|
||||
internal class FastDictionaryAppCache : IAppCache
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Lazy<object>> _items
|
||||
= new ConcurrentDictionary<string, Lazy<object>>();
|
||||
/// <summary>
|
||||
/// Gets the internal items dictionary, for tests only!
|
||||
/// </summary>
|
||||
internal readonly ConcurrentDictionary<string, Lazy<object>> Items = new ConcurrentDictionary<string, Lazy<object>>();
|
||||
|
||||
// for tests
|
||||
internal ConcurrentDictionary<string, Lazy<object>> Items => _items;
|
||||
|
||||
public void ClearAllCache()
|
||||
/// <inheritdoc />
|
||||
public object Get(string cacheKey)
|
||||
{
|
||||
_items.Clear();
|
||||
Items.TryGetValue(cacheKey, out var result); // else null
|
||||
return result == null ? null : FastDictionaryAppCacheBase.GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public void ClearCacheItem(string key)
|
||||
/// <inheritdoc />
|
||||
public object Get(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
_items.TryRemove(key, out _);
|
||||
var result = Items.GetOrAdd(cacheKey, k => FastDictionaryAppCacheBase.GetSafeLazy(getCacheItem));
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (!(value is FastDictionaryAppCacheBase.ExceptionHolder eh))
|
||||
return value;
|
||||
|
||||
// and... it's in the cache anyway - so contrary to other cache providers,
|
||||
// which would trick with GetSafeLazyValue, we need to remove by ourselves,
|
||||
// in order NOT to cache exceptions
|
||||
|
||||
Items.TryRemove(cacheKey, out result);
|
||||
eh.Exception.Throw(); // throw once!
|
||||
return null; // never reached
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes(string typeName)
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
return Items
|
||||
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
|
||||
.Select(kvp => FastDictionaryAppCacheBase.GetSafeLazyValue(kvp.Value))
|
||||
.Where(x => x != null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
return Items
|
||||
.Where(kvp => compiled.IsMatch(kvp.Key))
|
||||
.Select(kvp => FastDictionaryAppCacheBase.GetSafeLazyValue(kvp.Value))
|
||||
.Where(x => x != null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear()
|
||||
{
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Clear(string key)
|
||||
{
|
||||
Items.TryRemove(key, out _);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType(string typeName)
|
||||
{
|
||||
var type = TypeFinder.GetTypeByName(typeName);
|
||||
if (type == null) return;
|
||||
var isInterface = type.IsInterface;
|
||||
|
||||
foreach (var kvp in _items
|
||||
foreach (var kvp in Items
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
// otherwise remove exact types (not inherited types)
|
||||
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
|
||||
}))
|
||||
_items.TryRemove(kvp.Key, out _);
|
||||
Items.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes<T>()
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
|
||||
foreach (var kvp in _items
|
||||
foreach (var kvp in Items
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
// otherwise remove exact types (not inherited types)
|
||||
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
|
||||
}))
|
||||
_items.TryRemove(kvp.Key, out _);
|
||||
Items.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
/// <inheritdoc />
|
||||
public void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
|
||||
foreach (var kvp in _items
|
||||
foreach (var kvp in Items
|
||||
.Where(x =>
|
||||
{
|
||||
// entry.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// compare on exact type, don't use "is"
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
|
||||
if (value == null) return true;
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
@@ -88,60 +139,24 @@ namespace Umbraco.Core.Cache
|
||||
// run predicate on the 'public key' part only, ie without prefix
|
||||
&& predicate(x.Key, (T)value);
|
||||
}))
|
||||
_items.TryRemove(kvp.Key, out _);
|
||||
Items.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
|
||||
public void ClearCacheByKeySearch(string keyStartsWith)
|
||||
/// <inheritdoc />
|
||||
public void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
foreach (var ikvp in _items
|
||||
foreach (var ikvp in Items
|
||||
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)))
|
||||
_items.TryRemove(ikvp.Key, out _);
|
||||
Items.TryRemove(ikvp.Key, out _);
|
||||
}
|
||||
|
||||
public void ClearCacheByKeyExpression(string regexString)
|
||||
/// <inheritdoc />
|
||||
public void ClearByRegex(string regex)
|
||||
{
|
||||
foreach (var ikvp in _items
|
||||
.Where(kvp => Regex.IsMatch(kvp.Key, regexString)))
|
||||
_items.TryRemove(ikvp.Key, out _);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return _items
|
||||
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
|
||||
.Select(kvp => DictionaryCacheProviderBase.GetSafeLazyValue(kvp.Value))
|
||||
.Where(x => x != null);
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return _items
|
||||
.Where(kvp => Regex.IsMatch(kvp.Key, regexString))
|
||||
.Select(kvp => DictionaryCacheProviderBase.GetSafeLazyValue(kvp.Value))
|
||||
.Where(x => x != null);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
_items.TryGetValue(cacheKey, out var result); // else null
|
||||
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
var result = _items.GetOrAdd(cacheKey, k => DictionaryCacheProviderBase.GetSafeLazy(getCacheItem));
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (!(value is DictionaryCacheProviderBase.ExceptionHolder eh))
|
||||
return value;
|
||||
|
||||
// and... it's in the cache anyway - so contrary to other cache providers,
|
||||
// which would trick with GetSafeLazyValue, we need to remove by ourselves,
|
||||
// in order NOT to cache exceptions
|
||||
|
||||
_items.TryRemove(cacheKey, out result);
|
||||
eh.Exception.Throw(); // throw once!
|
||||
return null; // never reached
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
foreach (var ikvp in Items
|
||||
.Where(kvp => compiled.IsMatch(kvp.Key)))
|
||||
Items.TryRemove(ikvp.Key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
+129
-113
@@ -8,7 +8,10 @@ using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
internal abstract class DictionaryCacheProviderBase : ICacheProvider
|
||||
/// <summary>
|
||||
/// Provides a base class to fast, dictionary-based <see cref="IAppCache"/> implementations.
|
||||
/// </summary>
|
||||
internal abstract class FastDictionaryAppCacheBase : IAppCache
|
||||
{
|
||||
// prefix cache keys so we know which one are ours
|
||||
protected const string CacheItemPrefix = "umbrtmche";
|
||||
@@ -16,82 +19,75 @@ namespace Umbraco.Core.Cache
|
||||
// an object that represent a value that has not been created yet
|
||||
protected internal static readonly object ValueNotCreated = new object();
|
||||
|
||||
// manupulate the underlying cache entries
|
||||
// these *must* be called from within the appropriate locks
|
||||
// and use the full prefixed cache keys
|
||||
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
|
||||
protected abstract void RemoveEntry(string key);
|
||||
protected abstract object GetEntry(string key);
|
||||
#region IAppCache
|
||||
|
||||
// read-write lock the underlying cache
|
||||
//protected abstract IDisposable ReadLock { get; }
|
||||
//protected abstract IDisposable WriteLock { get; }
|
||||
|
||||
protected abstract void EnterReadLock();
|
||||
protected abstract void ExitReadLock();
|
||||
protected abstract void EnterWriteLock();
|
||||
protected abstract void ExitWriteLock();
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string key)
|
||||
{
|
||||
return string.Format("{0}-{1}", CacheItemPrefix, key);
|
||||
}
|
||||
|
||||
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
|
||||
{
|
||||
// try to generate the value and if it fails,
|
||||
// wrap in an ExceptionHolder - would be much simpler
|
||||
// to just use lazy.IsValueFaulted alas that field is
|
||||
// internal
|
||||
return new Lazy<object>(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ExceptionHolder(ExceptionDispatchInfo.Capture(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differenciate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
|
||||
// if execution has thrown then lazy.IsValueCreated is false
|
||||
// and lazy.IsValueFaulted is true (but internal) so we use our
|
||||
// own exception holder (see Lazy<T> source code) to return null
|
||||
if (lazy.Value is ExceptionHolder) return null;
|
||||
|
||||
// we have a value and execution has not thrown so returning
|
||||
// here does not throw - unless we're re-entering, take care of it
|
||||
key = GetCacheKey(key);
|
||||
Lazy<object> result;
|
||||
try
|
||||
{
|
||||
return lazy.Value;
|
||||
EnterReadLock();
|
||||
result = GetEntry(key) as Lazy<object>; // null if key not found
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
finally
|
||||
{
|
||||
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
|
||||
ExitReadLock();
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
internal class ExceptionHolder
|
||||
/// <inheritdoc />
|
||||
public abstract object Get(string key, Func<object> factory);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
public ExceptionHolder(ExceptionDispatchInfo e)
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
try
|
||||
{
|
||||
Exception = e;
|
||||
EnterReadLock();
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitReadLock();
|
||||
}
|
||||
|
||||
public ExceptionDispatchInfo Exception { get; }
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
#region Clear
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
try
|
||||
{
|
||||
EnterReadLock();
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => compiled.IsMatch(((string)x.Key).Substring(plen)))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
}
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -106,7 +102,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear(string key)
|
||||
{
|
||||
var cacheKey = GetCacheKey(key);
|
||||
try
|
||||
@@ -120,7 +117,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType(string typeName)
|
||||
{
|
||||
var type = TypeFinder.GetTypeByName(typeName);
|
||||
if (type == null) return;
|
||||
@@ -149,7 +147,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
@@ -178,7 +177,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
@@ -210,7 +210,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
try
|
||||
@@ -227,14 +228,16 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
try
|
||||
{
|
||||
EnterWriteLock();
|
||||
foreach (var entry in GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.Where(x => compiled.IsMatch(((string)x.Key).Substring(plen)))
|
||||
.ToArray())
|
||||
RemoveEntry((string) entry.Key);
|
||||
}
|
||||
@@ -246,67 +249,80 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
#region Dictionary
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
// manipulate the underlying cache entries
|
||||
// these *must* be called from within the appropriate locks
|
||||
// and use the full prefixed cache keys
|
||||
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
|
||||
protected abstract void RemoveEntry(string key);
|
||||
protected abstract object GetEntry(string key);
|
||||
|
||||
// read-write lock the underlying cache
|
||||
//protected abstract IDisposable ReadLock { get; }
|
||||
//protected abstract IDisposable WriteLock { get; }
|
||||
|
||||
protected abstract void EnterReadLock();
|
||||
protected abstract void ExitReadLock();
|
||||
protected abstract void EnterWriteLock();
|
||||
protected abstract void ExitWriteLock();
|
||||
|
||||
protected string GetCacheKey(string key)
|
||||
{
|
||||
var plen = CacheItemPrefix.Length + 1;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
try
|
||||
{
|
||||
EnterReadLock();
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitReadLock();
|
||||
}
|
||||
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
return $"{CacheItemPrefix}-{key}";
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
var plen = prefix.Length;
|
||||
IEnumerable<DictionaryEntry> entries;
|
||||
try
|
||||
// try to generate the value and if it fails,
|
||||
// wrap in an ExceptionHolder - would be much simpler
|
||||
// to just use lazy.IsValueFaulted alas that field is
|
||||
// internal
|
||||
return new Lazy<object>(() =>
|
||||
{
|
||||
EnterReadLock();
|
||||
entries = GetDictionaryEntries()
|
||||
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null); // backward compat, don't store null values in the cache
|
||||
try
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ExceptionHolder(ExceptionDispatchInfo.Capture(e));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
Lazy<object> result;
|
||||
// if onlyIfValueIsCreated, do not trigger value creation
|
||||
// must return something, though, to differentiate from null values
|
||||
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
|
||||
|
||||
// if execution has thrown then lazy.IsValueCreated is false
|
||||
// and lazy.IsValueFaulted is true (but internal) so we use our
|
||||
// own exception holder (see Lazy<T> source code) to return null
|
||||
if (lazy.Value is ExceptionHolder) return null;
|
||||
|
||||
// we have a value and execution has not thrown so returning
|
||||
// here does not throw - unless we're re-entering, take care of it
|
||||
try
|
||||
{
|
||||
EnterReadLock();
|
||||
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
|
||||
return lazy.Value;
|
||||
}
|
||||
finally
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
ExitReadLock();
|
||||
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
|
||||
}
|
||||
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
internal class ExceptionHolder
|
||||
{
|
||||
public ExceptionHolder(ExceptionDispatchInfo e)
|
||||
{
|
||||
Exception = e;
|
||||
}
|
||||
|
||||
public ExceptionDispatchInfo Exception { get; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Cache
|
||||
private readonly Func<TEntity, TId> _entityGetId;
|
||||
private readonly bool _expires;
|
||||
|
||||
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
|
||||
public FullDataSetRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
|
||||
: base(cache, scopeAccessor)
|
||||
{
|
||||
_entityGetId = entityGetId;
|
||||
@@ -55,11 +55,11 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
if (_expires)
|
||||
{
|
||||
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
|
||||
Cache.Insert(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities));
|
||||
Cache.Insert(key, () => new DeepCloneableList<TEntity>(entities));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Umbraco.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
Cache.ClearCacheItem(GetEntityTypeCacheKey());
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+67
-90
@@ -7,54 +7,83 @@ using System.Web;
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache provider that caches items in the HttpContext.Items
|
||||
/// Implements a fast <see cref="IAppCache"/> on top of HttpContext.Items.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the Items collection is null, then this provider has no effect
|
||||
/// <para>If no current HttpContext items can be found (no current HttpContext,
|
||||
/// or no Items...) then this cache acts as a pass-through and does not cache
|
||||
/// anything.</para>
|
||||
/// </remarks>
|
||||
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
|
||||
internal class HttpRequestAppCache : FastDictionaryAppCacheBase
|
||||
{
|
||||
// context provider
|
||||
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
|
||||
// that is initialized with a method that returns the "current" context.
|
||||
// NOTE
|
||||
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
|
||||
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
|
||||
// so replace it with _context1 and _context2 below + a way to get context.Items.
|
||||
//private readonly Func<HttpContextBase> _context;
|
||||
|
||||
// NOTE
|
||||
// and then in almost 100% cases _context2 will be () => HttpContext.Current
|
||||
// so why not bring that logic in here and fallback on to HttpContext.Current when
|
||||
// _context1 is null?
|
||||
//private readonly HttpContextBase _context1;
|
||||
//private readonly Func<HttpContext> _context2;
|
||||
private readonly HttpContextBase _context;
|
||||
|
||||
private IDictionary ContextItems
|
||||
{
|
||||
//get { return _context1 != null ? _context1.Items : _context2().Items; }
|
||||
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
|
||||
}
|
||||
|
||||
private bool HasContextItems
|
||||
{
|
||||
get { return (_context != null && _context.Items != null) || HttpContext.Current != null; }
|
||||
}
|
||||
|
||||
// for unit tests
|
||||
public HttpRequestCacheProvider(HttpContextBase context)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class with a context, for unit tests!
|
||||
/// </summary>
|
||||
public HttpRequestAppCache(HttpContextBase context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// main constructor
|
||||
// will use HttpContext.Current
|
||||
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Will use HttpContext.Current.</para>
|
||||
/// fixme - should use IHttpContextAccessor
|
||||
/// </remarks>
|
||||
public HttpRequestAppCache()
|
||||
{ }
|
||||
|
||||
private IDictionary ContextItems => _context?.Items ?? HttpContext.Current?.Items;
|
||||
|
||||
private bool HasContextItems => _context?.Items != null || HttpContext.Current != null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Get(string key, Func<object> factory)
|
||||
{
|
||||
//_context2 = context;
|
||||
//no place to cache so just return the callback result
|
||||
if (HasContextItems == false) return factory();
|
||||
|
||||
key = GetCacheKey(key);
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
try
|
||||
{
|
||||
EnterWriteLock();
|
||||
result = ContextItems[key] as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = GetSafeLazy(factory);
|
||||
ContextItems[key] = result;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitWriteLock();
|
||||
}
|
||||
|
||||
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
|
||||
// exceptions (but try again and again) and silently eat them - however at
|
||||
// some point we have to report them - so need to re-throw here
|
||||
|
||||
// this does not throw anymore
|
||||
//return result.Value;
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (value is ExceptionHolder eh) eh.Exception.Throw(); // throw once!
|
||||
return value;
|
||||
}
|
||||
|
||||
#region Entries
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
@@ -62,7 +91,7 @@ namespace Umbraco.Core.Cache
|
||||
if (HasContextItems == false) return Enumerable.Empty<DictionaryEntry>();
|
||||
|
||||
return ContextItems.Cast<DictionaryEntry>()
|
||||
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
|
||||
.Where(x => x.Key is string s && s.StartsWith(prefix));
|
||||
}
|
||||
|
||||
protected override void RemoveEntry(string key)
|
||||
@@ -77,6 +106,8 @@ namespace Umbraco.Core.Cache
|
||||
return HasContextItems ? ContextItems[key] : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lock
|
||||
|
||||
private bool _entered;
|
||||
@@ -103,59 +134,5 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
//no place to cache so just return the callback result
|
||||
if (HasContextItems == false) return getCacheItem();
|
||||
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
try
|
||||
{
|
||||
EnterWriteLock();
|
||||
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
// more than one thread to reach the return statement below and throw - accepted.
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = GetSafeLazy(getCacheItem);
|
||||
ContextItems[cacheKey] = result;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ExitWriteLock();
|
||||
}
|
||||
|
||||
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
|
||||
// exceptions (but try again and again) and silently eat them - however at
|
||||
// some point we have to report them - so need to re-throw here
|
||||
|
||||
// this does not throw anymore
|
||||
//return result.Value;
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (value is ExceptionHolder eh) eh.Exception.Throw(); // throw once!
|
||||
return value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
#endregion
|
||||
|
||||
private class NoopLocker : DisposableObjectSlim
|
||||
{
|
||||
protected override void DisposeResources()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an application cache.
|
||||
/// </summary>
|
||||
public interface IAppCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an item identified by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <returns>The item, or null if the item was not found.</returns>
|
||||
object Get(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates an item identified by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <param name="factory">A factory function that can create the item.</param>
|
||||
/// <returns>The item.</returns>
|
||||
object Get(string key, Func<object> factory);
|
||||
|
||||
/// <summary>
|
||||
/// Gets items with a key starting with the specified value.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The StartsWith value to use in the search.</param>
|
||||
/// <returns>Items matching the search.</returns>
|
||||
IEnumerable<object> SearchByKey(string keyStartsWith);
|
||||
|
||||
/// <summary>
|
||||
/// Gets items with a key matching a regular expression.
|
||||
/// </summary>
|
||||
/// <param name="regex">The regular expression.</param>
|
||||
/// <returns>Items matching the search.</returns>
|
||||
IEnumerable<object> SearchByRegex(string regex);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all items from the cache.
|
||||
/// </summary>
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item identified by its key from the cache.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
void Clear(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items of a specified type from the cache.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the type to remove.</param>
|
||||
/// <remarks>
|
||||
/// <para>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</para>
|
||||
/// <para>Performs a case-sensitive search.</para>
|
||||
/// </remarks>
|
||||
void ClearOfType(string typeName);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items of a specified type from the cache.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items to remove.</typeparam>
|
||||
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</remarks>
|
||||
void ClearOfType<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Removes items of a specified type from the cache.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items to remove.</typeparam>
|
||||
/// <param name="predicate">The predicate to satisfy.</param>
|
||||
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</remarks>
|
||||
void ClearOfType<T>(Func<string, T, bool> predicate);
|
||||
|
||||
/// <summary>
|
||||
/// Clears items with a key starting with the specified value.
|
||||
/// </summary>
|
||||
/// <param name="keyStartsWith">The StartsWith value to use in the search.</param>
|
||||
void ClearByKey(string keyStartsWith);
|
||||
|
||||
/// <summary>
|
||||
/// Clears items with a key matching a regular expression.
|
||||
/// </summary>
|
||||
/// <param name="regex">The regular expression.</param>
|
||||
void ClearByRegex(string regex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Web.Caching;
|
||||
|
||||
// fixme should this be/support non-web?
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an application cache that support cache policies.
|
||||
/// </summary>
|
||||
/// <remarks>A cache policy can be used to cache with timeouts,
|
||||
/// or depending on files, and with a remove callback, etc.</remarks>
|
||||
public interface IAppPolicyCache : IAppCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an item identified by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <param name="factory">A factory function that can create the item.</param>
|
||||
/// <param name="timeout">An optional cache timeout.</param>
|
||||
/// <param name="isSliding">An optional value indicating whether the cache timeout is sliding (default is false).</param>
|
||||
/// <param name="priority">An optional cache priority (default is Normal).</param>
|
||||
/// <param name="removedCallback">An optional callback to handle removals.</param>
|
||||
/// <param name="dependentFiles">Files the cache entry depends on.</param>
|
||||
/// <returns>The item.</returns>
|
||||
object Get(
|
||||
string key,
|
||||
Func<object> factory,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts an item.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
/// <param name="factory">A factory function that can create the item.</param>
|
||||
/// <param name="timeout">An optional cache timeout.</param>
|
||||
/// <param name="isSliding">An optional value indicating whether the cache timeout is sliding (default is false).</param>
|
||||
/// <param name="priority">An optional cache priority (default is Normal).</param>
|
||||
/// <param name="removedCallback">An optional callback to handle removals.</param>
|
||||
/// <param name="dependentFiles">Files the cache entry depends on.</param>
|
||||
void Insert(
|
||||
string key,
|
||||
Func<object> factory,
|
||||
TimeSpan? timeout = null,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for implementing a basic cache provider
|
||||
/// </summary>
|
||||
public interface ICacheProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Removes all items from the cache.
|
||||
/// </summary>
|
||||
void ClearAllCache();
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from the cache, identified by its key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key of the item.</param>
|
||||
void ClearCacheItem(string key);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items from the cache, of a specified type.
|
||||
/// </summary>
|
||||
/// <param name="typeName">The name of the type to remove.</param>
|
||||
/// <remarks>
|
||||
/// <para>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</para>
|
||||
/// <para>Performs a case-sensitive search.</para>
|
||||
/// </remarks>
|
||||
void ClearCacheObjectTypes(string typeName);
|
||||
|
||||
/// <summary>
|
||||
/// Removes items from the cache, of a specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items to remove.</typeparam>
|
||||
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</remarks>
|
||||
void ClearCacheObjectTypes<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Removes items from the cache, of a specified type, satisfying a predicate.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the items to remove.</typeparam>
|
||||
/// <param name="predicate">The predicate to satisfy.</param>
|
||||
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
|
||||
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
|
||||
/// the specified type are not removed).</remarks>
|
||||
void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate);
|
||||
|
||||
void ClearCacheByKeySearch(string keyStartsWith);
|
||||
void ClearCacheByKeyExpression(string regexString);
|
||||
|
||||
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
|
||||
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
|
||||
|
||||
/// <summary>
|
||||
/// Returns an item with a given key
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
object GetCacheItem(string cacheKey);
|
||||
|
||||
object GetCacheItem(string cacheKey, Func<object> getCacheItem);
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Caching;
|
||||
using System.Text;
|
||||
using System.Web.Caching;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class for implementing a runtime cache provider
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public interface IRuntimeCacheProvider : ICacheProvider
|
||||
{
|
||||
object GetCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
|
||||
void InsertCacheItem(
|
||||
string cacheKey,
|
||||
Func<object> getCacheItem,
|
||||
TimeSpan? timeout = null,
|
||||
bool isSliding = false,
|
||||
CacheItemPriority priority = CacheItemPriority.Normal,
|
||||
CacheItemRemovedCallback removedCallback = null,
|
||||
string[] dependentFiles = null);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a dictionary of <see cref="IAppPolicyCache"/> for types.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Isolated caches are used by e.g. repositories, to ensure that each cached entity
|
||||
/// type has its own cache, so that lookups are fast and the repository does not need to
|
||||
/// search through all keys on a global scale.</para>
|
||||
/// </remarks>
|
||||
public class IsolatedCaches : AppPolicedCacheDictionary<Type>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IsolatedCaches"/> class.
|
||||
/// </summary>
|
||||
/// <param name="cacheFactory"></param>
|
||||
public IsolatedCaches(Func<Type, IAppPolicyCache> cacheFactory)
|
||||
: base(cacheFactory)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a cache.
|
||||
/// </summary>
|
||||
public IAppPolicyCache GetOrCreate<T>()
|
||||
=> GetOrCreate(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a cache.
|
||||
/// </summary>
|
||||
public Attempt<IAppPolicyCache> Get<T>()
|
||||
=> Get(typeof(T));
|
||||
|
||||
/// <summary>
|
||||
/// Clears a cache.
|
||||
/// </summary>
|
||||
public void ClearCache<T>()
|
||||
=> ClearCache(typeof(T));
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to get/create/manipulate isolated runtime cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is useful for repository level caches to ensure that cache lookups by key are fast so
|
||||
/// that the repository doesn't need to search through all keys on a global scale.
|
||||
/// </remarks>
|
||||
public class IsolatedRuntimeCache
|
||||
{
|
||||
internal Func<Type, IRuntimeCacheProvider> CacheFactory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that allows specifying a factory for the type of runtime isolated cache to create
|
||||
/// </summary>
|
||||
/// <param name="cacheFactory"></param>
|
||||
public IsolatedRuntimeCache(Func<Type, IRuntimeCacheProvider> cacheFactory)
|
||||
{
|
||||
CacheFactory = cacheFactory;
|
||||
}
|
||||
|
||||
private readonly ConcurrentDictionary<Type, IRuntimeCacheProvider> _isolatedCache = new ConcurrentDictionary<Type, IRuntimeCacheProvider>();
|
||||
|
||||
/// <summary>
|
||||
/// Returns an isolated runtime cache for a given type
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache<T>()
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(typeof(T), type => CacheFactory(type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an isolated runtime cache for a given type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IRuntimeCacheProvider GetOrCreateCache(Type type)
|
||||
{
|
||||
return _isolatedCache.GetOrAdd(type, t => CacheFactory(t));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a cache by the type specified
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public Attempt<IRuntimeCacheProvider> GetCache<T>()
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryGetValue(typeof(T), out cache))
|
||||
{
|
||||
return Attempt.Succeed(cache);
|
||||
}
|
||||
return Attempt<IRuntimeCacheProvider>.Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all values inside this isolated runtime cache
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public void ClearCache<T>()
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryGetValue(typeof(T), out cache))
|
||||
{
|
||||
cache.ClearAllCache();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all of the isolated caches
|
||||
/// </summary>
|
||||
public void ClearAllCaches()
|
||||
{
|
||||
foreach (var key in _isolatedCache.Keys)
|
||||
{
|
||||
IRuntimeCacheProvider cache;
|
||||
if (_isolatedCache.TryRemove(key, out cache))
|
||||
{
|
||||
cache.ClearAllCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonCacheRefresherBase{TInstanceType}"/>.
|
||||
/// </summary>
|
||||
/// <param name="cacheHelper">A cache helper.</param>
|
||||
protected JsonCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected JsonCacheRefresherBase(AppCaches appCaches) : base(appCaches)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements <see cref="IAppPolicyCache"/> and do not cache.
|
||||
/// </summary>
|
||||
public class NoAppCache : IAppPolicyCache
|
||||
{
|
||||
private NoAppCache() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the singleton instance.
|
||||
/// </summary>
|
||||
public static NoAppCache Instance { get; } = new NoAppCache();
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string cacheKey)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual object Get(string cacheKey, Func<object> factory)
|
||||
{
|
||||
return factory();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
return factory();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear(string key)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType(string typeName)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>()
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByKey(string keyStartsWith)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByRegex(string regex)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public static NoCacheRepositoryCachePolicy<TEntity, TId> Instance { get; } = new NoCacheRepositoryCachePolicy<TEntity, TId>();
|
||||
|
||||
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
|
||||
public IRepositoryCachePolicy<TEntity, TId> Scoped(IAppPolicyCache runtimeCache, IScope scope)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Caching;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cache provider that does not cache anything.
|
||||
/// </summary>
|
||||
public class NullCacheProvider : IRuntimeCacheProvider
|
||||
{
|
||||
private NullCacheProvider() { }
|
||||
|
||||
public static NullCacheProvider Instance { get; } = new NullCacheProvider();
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{ }
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{ }
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
return default(object);
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
return getCacheItem();
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
+144
-139
@@ -11,31 +11,142 @@ using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cache provider that caches item in a <see cref="MemoryCache"/>.
|
||||
/// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache
|
||||
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="ObjectCache"/>.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="MemoryCache"/> is created with name "in-memory". That name is
|
||||
/// used to retrieve configuration options. It does not identify the memory cache, i.e.
|
||||
/// each instance of this class has its own, independent, memory cache.</remarks>
|
||||
public class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
|
||||
public class ObjectCacheAppCache : IAppPolicyCache
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
internal ObjectCache MemoryCache;
|
||||
|
||||
/// <summary>
|
||||
/// Used for debugging
|
||||
/// Initializes a new instance of the <see cref="ObjectCacheAppCache"/>.
|
||||
/// </summary>
|
||||
internal Guid InstanceId { get; private set; }
|
||||
|
||||
public ObjectCacheRuntimeCacheProvider()
|
||||
public ObjectCacheAppCache()
|
||||
{
|
||||
// the MemoryCache is created with name "in-memory". That name is
|
||||
// used to retrieve configuration options. It does not identify the memory cache, i.e.
|
||||
// each instance of this class has its own, independent, memory cache.
|
||||
MemoryCache = new MemoryCache("in-memory");
|
||||
InstanceId = Guid.NewGuid();
|
||||
}
|
||||
|
||||
#region Clear
|
||||
/// <summary>
|
||||
/// Gets the internal memory cache, for tests only!
|
||||
/// </summary>
|
||||
internal ObjectCache MemoryCache { get; private set; }
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
/// <inheritdoc />
|
||||
public object Get(string key)
|
||||
{
|
||||
Lazy<object> result;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
result = MemoryCache.Get(key) as Lazy<object>; // null if key not found
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return result == null ? null : FastDictionaryAppCacheBase.GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory)
|
||||
{
|
||||
return Get(key, factory, null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByKey(string keyStartsWith)
|
||||
{
|
||||
KeyValuePair<string, object>[] entries;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
entries = MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<object> SearchByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
|
||||
KeyValuePair<string, object>[] entries;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
entries = MemoryCache
|
||||
.Where(x => compiled.IsMatch(x.Key))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// see notes in HttpRuntimeAppCache
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(key) as Lazy<object>;
|
||||
if (result == null || FastDictionaryAppCacheBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
//NOTE: This does an add or update
|
||||
MemoryCache.Set(key, result, policy);
|
||||
}
|
||||
}
|
||||
|
||||
//return result.Value;
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (value is FastDictionaryAppCacheBase.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
|
||||
var value = result.Value; // force evaluation now
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
//NOTE: This does an add or update
|
||||
MemoryCache.Set(key, result, policy);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -50,7 +161,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
/// <inheritdoc />
|
||||
public virtual void Clear(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -65,7 +177,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType(string typeName)
|
||||
{
|
||||
var type = TypeFinder.GetTypeByName(typeName);
|
||||
if (type == null) return;
|
||||
@@ -79,7 +192,7 @@ namespace Umbraco.Core.Cache
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
// otherwise remove exact types (not inherited types)
|
||||
@@ -96,12 +209,13 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>()
|
||||
{
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
var typeOfT = typeof (T);
|
||||
var typeOfT = typeof(T);
|
||||
var isInterface = typeOfT.IsInterface;
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x =>
|
||||
@@ -109,7 +223,7 @@ namespace Umbraco.Core.Cache
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
// otherwise remove exact types (not inherited types)
|
||||
@@ -127,7 +241,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -140,7 +255,7 @@ namespace Umbraco.Core.Cache
|
||||
// x.Value is Lazy<object> and not null, its value may be null
|
||||
// remove null values as well, does not hurt
|
||||
// get non-created as NonCreatedValue & exceptions as null
|
||||
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
|
||||
if (value == null) return true;
|
||||
|
||||
// if T is an interface remove anything that implements that interface
|
||||
@@ -159,7 +274,8 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByKey(string keyStartsWith)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -177,13 +293,16 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
/// <inheritdoc />
|
||||
public virtual void ClearByRegex(string regex)
|
||||
{
|
||||
var compiled = new Regex(regex, RegexOptions.Compiled);
|
||||
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
foreach (var key in MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.Where(x => compiled.IsMatch(x.Key))
|
||||
.Select(x => x.Key)
|
||||
.ToArray()) // ToArray required to remove
|
||||
MemoryCache.Remove(key);
|
||||
@@ -195,120 +314,6 @@ namespace Umbraco.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
KeyValuePair<string, object>[] entries;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
entries = MemoryCache
|
||||
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
KeyValuePair<string, object>[] entries;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
entries = MemoryCache
|
||||
.Where(x => Regex.IsMatch(x.Key, regexString))
|
||||
.ToArray(); // evaluate while locked
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return entries
|
||||
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
|
||||
.Where(x => x != null) // backward compat, don't store null values in the cache
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey)
|
||||
{
|
||||
Lazy<object> result;
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return GetCacheItem(cacheKey, getCacheItem, null);
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// see notes in HttpRuntimeCacheProvider
|
||||
|
||||
Lazy<object> result;
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = MemoryCache.Get(cacheKey) as Lazy<object>;
|
||||
if (result == null || DictionaryCacheProviderBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
//NOTE: This does an add or update
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
}
|
||||
|
||||
//return result.Value;
|
||||
|
||||
var value = result.Value; // will not throw (safe lazy)
|
||||
if (value is DictionaryCacheProviderBase.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
|
||||
return value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
|
||||
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
|
||||
var value = result.Value; // force evaluation now
|
||||
if (value == null) return; // do not store null values (backward compat)
|
||||
|
||||
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
|
||||
//NOTE: This does an add or update
|
||||
MemoryCache.Set(cacheKey, result, policy);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
@@ -15,8 +15,8 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PayloadCacheRefresherBase{TInstanceType, TPayload}"/>.
|
||||
/// </summary>
|
||||
/// <param name="cacheHelper">A cache helper.</param>
|
||||
protected PayloadCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected PayloadCacheRefresherBase(AppCaches appCaches) : base(appCaches)
|
||||
{ }
|
||||
|
||||
#region Json
|
||||
|
||||
@@ -13,16 +13,16 @@ namespace Umbraco.Core.Cache
|
||||
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
private readonly IRuntimeCacheProvider _globalCache;
|
||||
private readonly IAppPolicyCache _globalCache;
|
||||
private readonly IScopeAccessor _scopeAccessor;
|
||||
|
||||
protected RepositoryCachePolicyBase(IRuntimeCacheProvider globalCache, IScopeAccessor scopeAccessor)
|
||||
protected RepositoryCachePolicyBase(IAppPolicyCache globalCache, IScopeAccessor scopeAccessor)
|
||||
{
|
||||
_globalCache = globalCache ?? throw new ArgumentNullException(nameof(globalCache));
|
||||
_scopeAccessor = scopeAccessor ?? throw new ArgumentNullException(nameof(scopeAccessor));
|
||||
}
|
||||
|
||||
protected IRuntimeCacheProvider Cache
|
||||
protected IAppPolicyCache Cache
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -32,9 +32,9 @@ namespace Umbraco.Core.Cache
|
||||
case RepositoryCacheMode.Default:
|
||||
return _globalCache;
|
||||
case RepositoryCacheMode.Scoped:
|
||||
return ambientScope.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
|
||||
return ambientScope.IsolatedCaches.GetOrCreate<TEntity>();
|
||||
case RepositoryCacheMode.None:
|
||||
return NullCacheProvider.Instance;
|
||||
return NoAppCache.Instance;
|
||||
default:
|
||||
throw new NotSupportedException($"Repository cache mode {ambientScope.RepositoryCacheMode} is not supported.");
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Cache
|
||||
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
: base(cache, scopeAccessor, options)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a cache provider that statically caches item in a concurrent dictionary.
|
||||
/// </summary>
|
||||
public class StaticCacheProvider : ICacheProvider
|
||||
{
|
||||
internal readonly ConcurrentDictionary<string, object> StaticCache = new ConcurrentDictionary<string, object>();
|
||||
|
||||
public virtual void ClearAllCache()
|
||||
{
|
||||
StaticCache.Clear();
|
||||
}
|
||||
|
||||
public virtual void ClearCacheItem(string key)
|
||||
{
|
||||
object val;
|
||||
StaticCache.TryRemove(key, out val);
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes(string typeName)
|
||||
{
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>()
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
|
||||
}
|
||||
|
||||
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
|
||||
{
|
||||
var typeOfT = typeof(T);
|
||||
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeySearch(string keyStartsWith)
|
||||
{
|
||||
StaticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
|
||||
}
|
||||
|
||||
public virtual void ClearCacheByKeyExpression(string regexString)
|
||||
{
|
||||
StaticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString));
|
||||
}
|
||||
|
||||
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
|
||||
{
|
||||
return (from KeyValuePair<string, object> c in StaticCache
|
||||
where c.Key.InvariantStartsWith(keyStartsWith)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
|
||||
{
|
||||
return (from KeyValuePair<string, object> c in StaticCache
|
||||
where Regex.IsMatch(c.Key, regexString)
|
||||
select c.Value).ToList();
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey)
|
||||
{
|
||||
var result = StaticCache[cacheKey];
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
{
|
||||
return StaticCache.GetOrAdd(cacheKey, key => getCacheItem());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,9 @@ namespace Umbraco.Core.Cache
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypedCacheRefresherBase{TInstanceType, TEntityType}"/>.
|
||||
/// </summary>
|
||||
/// <param name="cacheHelper">A cache helper.</param>
|
||||
protected TypedCacheRefresherBase(CacheHelper cacheHelper)
|
||||
: base(cacheHelper)
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected TypedCacheRefresherBase(AppCaches appCaches)
|
||||
: base(appCaches)
|
||||
{ }
|
||||
|
||||
#region Refresher
|
||||
|
||||
+44
-75
@@ -4,14 +4,14 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web.Caching;
|
||||
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
|
||||
|
||||
namespace Umbraco.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A CacheProvider that wraps the logic of the HttpRuntime.Cache
|
||||
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="System.Web.Caching.Cache"/>.
|
||||
/// </summary>
|
||||
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
|
||||
/// <remarks>The underlying cache is expected to be HttpRuntime.Cache.</remarks>
|
||||
internal class WebCachingAppCache : FastDictionaryAppCacheBase, IAppPolicyCache
|
||||
{
|
||||
// locker object that supports upgradeable read locking
|
||||
// does not need to support recursion if we implement the cache correctly and ensure
|
||||
@@ -21,16 +21,43 @@ namespace Umbraco.Core.Cache
|
||||
private readonly System.Web.Caching.Cache _cache;
|
||||
|
||||
/// <summary>
|
||||
/// Used for debugging
|
||||
/// Initializes a new instance of the <see cref="WebCachingAppCache"/> class.
|
||||
/// </summary>
|
||||
internal Guid InstanceId { get; private set; }
|
||||
|
||||
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
|
||||
public WebCachingAppCache(System.Web.Caching.Cache cache)
|
||||
{
|
||||
_cache = cache;
|
||||
InstanceId = Guid.NewGuid();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object Get(string key, Func<object> factory)
|
||||
{
|
||||
return Get(key, factory, null, dependentFiles: null);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
return Get(key, factory, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
Insert(key, factory, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#region Dictionary
|
||||
|
||||
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
|
||||
{
|
||||
const string prefix = CacheItemPrefix + "-";
|
||||
@@ -48,6 +75,8 @@ namespace Umbraco.Core.Cache
|
||||
return _cache.Get(key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lock
|
||||
|
||||
protected override void EnterReadLock()
|
||||
@@ -74,33 +103,9 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
|
||||
private object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
return GetCacheItem(cacheKey, getCacheItem, null, dependentFiles: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This overload is here for legacy purposes
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <param name="isSliding"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="removedCallback"></param>
|
||||
/// <param name="dependency"></param>
|
||||
/// <returns></returns>
|
||||
internal object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
cacheKey = GetCacheKey(cacheKey);
|
||||
key = GetCacheKey(key);
|
||||
|
||||
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
|
||||
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
|
||||
@@ -133,7 +138,7 @@ namespace Umbraco.Core.Cache
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
result = _cache.Get(key) as Lazy<object>; // null if key not found
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -145,7 +150,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
using (var lck = new UpgradeableReadLock(_locker))
|
||||
{
|
||||
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
|
||||
result = _cache.Get(key) as Lazy<object>; // null if key not found
|
||||
|
||||
// cannot create value within the lock, so if result.IsValueCreated is false, just
|
||||
// do nothing here - means that if creation throws, a race condition could cause
|
||||
@@ -153,13 +158,13 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
|
||||
{
|
||||
result = GetSafeLazy(getCacheItem);
|
||||
result = GetSafeLazy(factory);
|
||||
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
_cache.Insert(key, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,31 +180,7 @@ namespace Umbraco.Core.Cache
|
||||
return value;
|
||||
}
|
||||
|
||||
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
|
||||
/// <summary>
|
||||
/// This overload is here for legacy purposes
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="getCacheItem"></param>
|
||||
/// <param name="timeout"></param>
|
||||
/// <param name="isSliding"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <param name="removedCallback"></param>
|
||||
/// <param name="dependency"></param>
|
||||
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
private void Insert(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
|
||||
{
|
||||
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
|
||||
// and make sure we don't store a null value.
|
||||
@@ -225,17 +206,5 @@ namespace Umbraco.Core.Cache
|
||||
_locker.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
CacheDependency dependency = null;
|
||||
if (dependentFiles != null && dependentFiles.Any())
|
||||
{
|
||||
dependency = new CacheDependency(dependentFiles);
|
||||
}
|
||||
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -125,6 +125,18 @@ namespace Umbraco.Core.Collections
|
||||
|
||||
}
|
||||
|
||||
public void ReplaceAll(IEnumerable<TValue> values)
|
||||
{
|
||||
if (values == null) throw new ArgumentNullException(nameof(values));
|
||||
|
||||
Clear();
|
||||
|
||||
foreach (var value in values)
|
||||
{
|
||||
Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
if (!Indecies.ContainsKey(key)) return false;
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace Umbraco.Core.Composing.Composers
|
||||
// register others
|
||||
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Content);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Templates);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().RequestHandler);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoSettingsSection>().Security);
|
||||
|
||||
|
||||
@@ -73,11 +73,6 @@ namespace Umbraco.Core.Composing.Composers
|
||||
factory.GetInstance<CompiledPackageXmlParser>(), factory.GetInstance<IPackageActionRunner>(),
|
||||
new DirectoryInfo(IOHelper.GetRootDirectorySafe())));
|
||||
|
||||
//TODO: These are replaced in the web project - we need to declare them so that
|
||||
// something is wired up, just not sure this is very nice but will work for now.
|
||||
composition.RegisterUnique<IApplicationTreeService, EmptyApplicationTreeService>();
|
||||
composition.RegisterUnique<ISectionService, EmptySectionService>();
|
||||
|
||||
return composition;
|
||||
}
|
||||
|
||||
@@ -88,7 +83,7 @@ namespace Umbraco.Core.Composing.Composers
|
||||
/// <param name="packageRepoFileName"></param>
|
||||
/// <returns></returns>
|
||||
private static PackagesRepository CreatePackageRepository(IFactory factory, string packageRepoFileName)
|
||||
=> new PackagesRepository(
|
||||
=> new PackagesRepository(
|
||||
factory.GetInstance<IContentService>(), factory.GetInstance<IContentTypeService>(), factory.GetInstance<IDataTypeService>(), factory.GetInstance<IFileService>(), factory.GetInstance<IMacroService>(), factory.GetInstance<ILocalizationService>(), factory.GetInstance<IEntityXmlSerializer>(), factory.GetInstance<ILogger>(),
|
||||
packageRepoFileName);
|
||||
|
||||
@@ -116,7 +111,7 @@ namespace Umbraco.Core.Composing.Composers
|
||||
|
||||
return new LocalizedTextServiceFileSources(
|
||||
container.GetInstance<ILogger>(),
|
||||
container.GetInstance<CacheHelper>().RuntimeCache,
|
||||
container.GetInstance<AppCaches>(),
|
||||
mainLangFolder,
|
||||
pluginLangFolders.Concat(userLangFolders));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Composing
|
||||
public static void RegisterEssentials(this Composition composition,
|
||||
ILogger logger, IProfiler profiler, IProfilingLogger profilingLogger,
|
||||
IMainDom mainDom,
|
||||
CacheHelper appCaches,
|
||||
AppCaches appCaches,
|
||||
IUmbracoDatabaseFactory databaseFactory,
|
||||
TypeLoader typeLoader,
|
||||
IRuntimeState state)
|
||||
@@ -28,7 +28,6 @@ namespace Umbraco.Core.Composing
|
||||
composition.RegisterUnique(profilingLogger);
|
||||
composition.RegisterUnique(mainDom);
|
||||
composition.RegisterUnique(appCaches);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<CacheHelper>().RuntimeCache);
|
||||
composition.RegisterUnique(databaseFactory);
|
||||
composition.RegisterUnique(factory => factory.GetInstance<IUmbracoDatabaseFactory>().SqlContext);
|
||||
composition.RegisterUnique(typeLoader);
|
||||
|
||||
@@ -180,8 +180,8 @@ namespace Umbraco.Core.Composing
|
||||
public static ICultureDictionaryFactory CultureDictionaryFactory
|
||||
=> Factory.GetInstance<ICultureDictionaryFactory>();
|
||||
|
||||
public static CacheHelper ApplicationCache
|
||||
=> Factory.GetInstance<CacheHelper>();
|
||||
public static AppCaches AppCaches
|
||||
=> Factory.GetInstance<AppCaches>();
|
||||
|
||||
public static ServiceContext Services
|
||||
=> Factory.GetInstance<ServiceContext>();
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Umbraco.Core.Composing
|
||||
{
|
||||
private const string CacheKey = "umbraco-types.list";
|
||||
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly IAppPolicyCache _runtimeCache;
|
||||
private readonly IProfilingLogger _logger;
|
||||
|
||||
private readonly Dictionary<CompositeTypeTypeKey, TypeList> _types = new Dictionary<CompositeTypeTypeKey, TypeList>();
|
||||
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <param name="runtimeCache">The application runtime cache.</param>
|
||||
/// <param name="localTempStorage">Files storage mode.</param>
|
||||
/// <param name="logger">A profiling logger.</param>
|
||||
public TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger)
|
||||
public TypeLoader(IAppPolicyCache runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger)
|
||||
: this(runtimeCache, localTempStorage, logger, true)
|
||||
{ }
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Umbraco.Core.Composing
|
||||
/// <param name="localTempStorage">Files storage mode.</param>
|
||||
/// <param name="logger">A profiling logger.</param>
|
||||
/// <param name="detectChanges">Whether to detect changes using hashes.</param>
|
||||
internal TypeLoader(IRuntimeCacheProvider runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger, bool detectChanges)
|
||||
internal TypeLoader(IAppPolicyCache runtimeCache, LocalTempStorage localTempStorage, IProfilingLogger logger, bool detectChanges)
|
||||
{
|
||||
_runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
|
||||
_localTempStorage = localTempStorage == LocalTempStorage.Unknown ? LocalTempStorage.Default : localTempStorage;
|
||||
@@ -185,9 +185,7 @@ namespace Umbraco.Core.Composing
|
||||
// the app code folder and everything in it
|
||||
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(IOHelper.MapPath("~/App_Code")), false),
|
||||
// global.asax (the app domain also monitors this, if it changes will do a full restart)
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath("~/global.asax")), false),
|
||||
// trees.config - use the contents to create the hash since this gets resaved on every app startup!
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
|
||||
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath("~/global.asax")), false)
|
||||
}, _logger);
|
||||
|
||||
return _currentAssembliesHash;
|
||||
@@ -478,7 +476,7 @@ namespace Umbraco.Core.Composing
|
||||
var typesHashFilePath = GetTypesHashFilePath();
|
||||
DeleteFile(typesHashFilePath, FileDeleteTimeout);
|
||||
|
||||
_runtimeCache.ClearCacheItem(CacheKey);
|
||||
_runtimeCache.Clear(CacheKey);
|
||||
}
|
||||
|
||||
private Stream GetFileStream(string path, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, int timeoutMilliseconds)
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Umbraco.Core
|
||||
configs.Add(() => new CoreDebug());
|
||||
|
||||
// GridConfig depends on runtime caches, manifest parsers... and cannot be available during composition
|
||||
configs.Add<IGridConfig>(factory => new GridConfig(factory.GetInstance<ILogger>(), factory.GetInstance<IRuntimeCacheProvider>(), configDir, factory.GetInstance<IRuntimeState>().Debug));
|
||||
configs.Add<IGridConfig>(factory => new GridConfig(factory.GetInstance<ILogger>(), factory.GetInstance<AppCaches>(), configDir, factory.GetInstance<IRuntimeState>().Debug));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
{
|
||||
class GridConfig : IGridConfig
|
||||
{
|
||||
public GridConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo configFolder, bool isDebug)
|
||||
public GridConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, bool isDebug)
|
||||
{
|
||||
EditorsConfig = new GridEditorsConfig(logger, runtimeCache, configFolder, isDebug);
|
||||
EditorsConfig = new GridEditorsConfig(logger, appCaches, configFolder, isDebug);
|
||||
}
|
||||
|
||||
public IGridEditorsConfig EditorsConfig { get; }
|
||||
|
||||
@@ -13,14 +13,14 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
internal class GridEditorsConfig : IGridEditorsConfig
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IRuntimeCacheProvider _runtimeCache;
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly DirectoryInfo _configFolder;
|
||||
private readonly bool _isDebug;
|
||||
|
||||
public GridEditorsConfig(ILogger logger, IRuntimeCacheProvider runtimeCache, DirectoryInfo configFolder, bool isDebug)
|
||||
public GridEditorsConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, bool isDebug)
|
||||
{
|
||||
_logger = logger;
|
||||
_runtimeCache = runtimeCache;
|
||||
_appCaches = appCaches;
|
||||
_configFolder = configFolder;
|
||||
_isDebug = isDebug;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
List<GridEditor> GetResult()
|
||||
{
|
||||
// fixme - should use the common one somehow! + ignoring _appPlugins here!
|
||||
var parser = new ManifestParser(_runtimeCache, Current.ManifestValidators, _logger);
|
||||
var parser = new ManifestParser(_appCaches, Current.ManifestValidators, _logger);
|
||||
|
||||
var editors = new List<GridEditor>();
|
||||
var gridConfig = Path.Combine(_configFolder.FullName, "grid.editors.config.js");
|
||||
@@ -62,7 +62,7 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
//cache the result if debugging is disabled
|
||||
var result = _isDebug
|
||||
? GetResult()
|
||||
: _runtimeCache.GetCacheItem<List<GridEditor>>(typeof(GridEditorsConfig) + ".Editors",GetResult, TimeSpan.FromMinutes(10));
|
||||
: _appCaches.RuntimeCache.GetCacheItem<List<GridEditor>>(typeof(GridEditorsConfig) + ".Editors",GetResult, TimeSpan.FromMinutes(10));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
public interface ITemplatesSection : IUmbracoConfigurationSection
|
||||
{
|
||||
RenderingEngine DefaultRenderingEngine { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
@@ -12,8 +11,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
ISecuritySection Security { get; }
|
||||
|
||||
IRequestHandlerSection RequestHandler { get; }
|
||||
|
||||
ITemplatesSection Templates { get; }
|
||||
|
||||
ILoggingSection Logging { get; }
|
||||
|
||||
@@ -22,6 +19,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
IProvidersSection Providers { get; }
|
||||
|
||||
IWebRoutingSection WebRouting { get; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class TemplatesElement : UmbracoConfigurationElement, ITemplatesSection
|
||||
{
|
||||
[ConfigurationProperty("defaultRenderingEngine", IsRequired = true)]
|
||||
internal InnerTextConfigurationElement<RenderingEngine> DefaultRenderingEngine
|
||||
{
|
||||
get { return GetOptionalTextElement("defaultRenderingEngine", RenderingEngine.Mvc); }
|
||||
}
|
||||
|
||||
RenderingEngine ITemplatesSection.DefaultRenderingEngine
|
||||
{
|
||||
get { return DefaultRenderingEngine; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
|
||||
public class UmbracoSettingsSection : ConfigurationSection, IUmbracoSettingsSection
|
||||
{
|
||||
[ConfigurationProperty("backOffice")]
|
||||
@@ -32,12 +28,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return (RequestHandlerElement)this["requestHandler"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("templates")]
|
||||
internal TemplatesElement Templates
|
||||
{
|
||||
get { return (TemplatesElement)this["templates"]; }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("logging")]
|
||||
internal LoggingElement Logging
|
||||
{
|
||||
@@ -77,11 +67,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return RequestHandler; }
|
||||
}
|
||||
|
||||
ITemplatesSection IUmbracoSettingsSection.Templates
|
||||
{
|
||||
get { return Templates; }
|
||||
}
|
||||
|
||||
IBackOfficeSection IUmbracoSettingsSection.BackOffice
|
||||
{
|
||||
get { return BackOffice; }
|
||||
@@ -106,6 +91,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
get { return WebRouting; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace Umbraco.Core.IO
|
||||
private ShadowWrapper _partialViewsFileSystem;
|
||||
private ShadowWrapper _stylesheetsFileSystem;
|
||||
private ShadowWrapper _scriptsFileSystem;
|
||||
private ShadowWrapper _masterPagesFileSystem;
|
||||
private ShadowWrapper _mvcViewsFileSystem;
|
||||
|
||||
// well-known file systems lazy initialization
|
||||
@@ -102,16 +101,6 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFileSystem MasterPagesFileSystem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems();
|
||||
return _masterPagesFileSystem;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFileSystem MvcViewsFileSystem
|
||||
{
|
||||
@@ -135,14 +124,12 @@ namespace Umbraco.Core.IO
|
||||
var partialViewsFileSystem = new PhysicalFileSystem(SystemDirectories.PartialViews);
|
||||
var stylesheetsFileSystem = new PhysicalFileSystem(SystemDirectories.Css);
|
||||
var scriptsFileSystem = new PhysicalFileSystem(SystemDirectories.Scripts);
|
||||
var masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages);
|
||||
var mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews);
|
||||
|
||||
_macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", IsScoped);
|
||||
_partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", IsScoped);
|
||||
_stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, "css", IsScoped);
|
||||
_scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, "scripts", IsScoped);
|
||||
_masterPagesFileSystem = new ShadowWrapper(masterPagesFileSystem, "masterpages", IsScoped);
|
||||
_mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", IsScoped);
|
||||
|
||||
// fixme locking?
|
||||
@@ -150,7 +137,6 @@ namespace Umbraco.Core.IO
|
||||
_shadowWrappers.Add(_partialViewsFileSystem);
|
||||
_shadowWrappers.Add(_stylesheetsFileSystem);
|
||||
_shadowWrappers.Add(_scriptsFileSystem);
|
||||
_shadowWrappers.Add(_masterPagesFileSystem);
|
||||
_shadowWrappers.Add(_mvcViewsFileSystem);
|
||||
|
||||
return null;
|
||||
|
||||
@@ -25,11 +25,6 @@
|
||||
/// </summary>
|
||||
IFileSystem ScriptsFileSystem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the masterpages filesystem.
|
||||
/// </summary>
|
||||
IFileSystem MasterPagesFileSystem { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the MVC views filesystem.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,445 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Xml;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
internal class MasterPageHelper
|
||||
{
|
||||
private readonly IFileSystem _masterPageFileSystem;
|
||||
internal static readonly string DefaultMasterTemplate = SystemDirectories.Umbraco + "/masterpages/default.master";
|
||||
//private static readonly char[] NewLineChars = Environment.NewLine.ToCharArray();
|
||||
|
||||
public MasterPageHelper(IFileSystem masterPageFileSystem)
|
||||
{
|
||||
if (masterPageFileSystem == null) throw new ArgumentNullException("masterPageFileSystem");
|
||||
_masterPageFileSystem = masterPageFileSystem;
|
||||
}
|
||||
|
||||
public bool MasterPageExists(ITemplate t)
|
||||
{
|
||||
return _masterPageFileSystem.FileExists(GetFilePath(t));
|
||||
}
|
||||
|
||||
private string GetFilePath(ITemplate t)
|
||||
{
|
||||
return GetFilePath(t.Alias);
|
||||
}
|
||||
|
||||
private string GetFilePath(string alias)
|
||||
{
|
||||
return alias + ".master";
|
||||
}
|
||||
|
||||
public string CreateMasterPage(ITemplate t, ITemplateRepository templateRepo, bool overWrite = false)
|
||||
{
|
||||
string masterpageContent = "";
|
||||
|
||||
var filePath = GetFilePath(t);
|
||||
if (_masterPageFileSystem.FileExists(filePath) == false || overWrite)
|
||||
{
|
||||
masterpageContent = t.Content.IsNullOrWhiteSpace() ? CreateDefaultMasterPageContent(t, templateRepo) : t.Content;
|
||||
|
||||
var data = Encoding.UTF8.GetBytes(masterpageContent);
|
||||
var withBom = Encoding.UTF8.GetPreamble().Concat(data).ToArray();
|
||||
|
||||
using (var ms = new MemoryStream(withBom))
|
||||
{
|
||||
_masterPageFileSystem.AddFile(filePath, ms, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var s = _masterPageFileSystem.OpenFile(filePath))
|
||||
using (var tr = new StreamReader(s, Encoding.UTF8))
|
||||
{
|
||||
masterpageContent = tr.ReadToEnd();
|
||||
tr.Close();
|
||||
}
|
||||
}
|
||||
|
||||
return masterpageContent;
|
||||
}
|
||||
|
||||
//internal string GetFileContents(ITemplate t)
|
||||
//{
|
||||
// var masterpageContent = "";
|
||||
// if (_masterPageFileSystem.FileExists(GetFilePath(t)))
|
||||
// {
|
||||
// using (var s = _masterPageFileSystem.OpenFile(GetFilePath(t)))
|
||||
// using (var tr = new StreamReader(s))
|
||||
// {
|
||||
// masterpageContent = tr.ReadToEnd();
|
||||
// tr.Close();
|
||||
// }
|
||||
// }
|
||||
|
||||
// return masterpageContent;
|
||||
//}
|
||||
|
||||
public string UpdateMasterPageFile(ITemplate t, string currentAlias, ITemplateRepository templateRepo)
|
||||
{
|
||||
var template = UpdateMasterPageContent(t, currentAlias);
|
||||
UpdateChildTemplates(t, currentAlias, templateRepo);
|
||||
var filePath = GetFilePath(t);
|
||||
|
||||
var data = Encoding.UTF8.GetBytes(template);
|
||||
var withBom = Encoding.UTF8.GetPreamble().Concat(data).ToArray();
|
||||
|
||||
using (var ms = new MemoryStream(withBom))
|
||||
{
|
||||
_masterPageFileSystem.AddFile(filePath, ms, true);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
private string CreateDefaultMasterPageContent(ITemplate template, ITemplateRepository templateRepo)
|
||||
{
|
||||
var design = new StringBuilder();
|
||||
design.Append(GetMasterPageHeader(template) + Environment.NewLine);
|
||||
|
||||
if (template.MasterTemplateAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
var master = templateRepo.Get(template.MasterTemplateAlias);
|
||||
if (master != null)
|
||||
{
|
||||
foreach (var cpId in GetContentPlaceholderIds(master))
|
||||
{
|
||||
design.Append("<asp:content ContentPlaceHolderId=\"" + cpId + "\" runat=\"server\">" +
|
||||
Environment.NewLine +
|
||||
Environment.NewLine +
|
||||
"</asp:content>" +
|
||||
Environment.NewLine +
|
||||
Environment.NewLine);
|
||||
}
|
||||
|
||||
return design.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
design.Append(GetMasterContentElement(template) + Environment.NewLine);
|
||||
design.Append(template.Content + Environment.NewLine);
|
||||
design.Append("</asp:Content>" + Environment.NewLine);
|
||||
|
||||
return design.ToString();
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetContentPlaceholderIds(ITemplate template)
|
||||
{
|
||||
var retVal = new List<string>();
|
||||
|
||||
var mp = template.Content;
|
||||
var path = "<asp:ContentPlaceHolder+(\\s+[a-zA-Z]+\\s*=\\s*(\"([^\"]*)\"|'([^']*)'))*\\s*/?>";
|
||||
var r = new Regex(path, RegexOptions.IgnoreCase);
|
||||
var m = r.Match(mp);
|
||||
|
||||
while (m.Success)
|
||||
{
|
||||
var cc = m.Groups[3].Captures;
|
||||
retVal.AddRange(cc.Cast<Capture>().Where(c => c.Value != "server").Select(c => c.Value));
|
||||
|
||||
m = m.NextMatch();
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private static string UpdateMasterPageContent(ITemplate template, string currentAlias)
|
||||
{
|
||||
var masterPageContent = template.Content;
|
||||
|
||||
if (string.IsNullOrEmpty(currentAlias) == false && currentAlias != template.Alias)
|
||||
{
|
||||
var masterHeader =
|
||||
masterPageContent.Substring(0, masterPageContent.IndexOf("%>", StringComparison.Ordinal) + 2).Trim(
|
||||
Environment.NewLine.ToCharArray());
|
||||
|
||||
// find the masterpagefile attribute
|
||||
var m = Regex.Matches(masterHeader, "(?<attributeName>\\S*)=\"(?<attributeValue>[^\"]*)\"",
|
||||
RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
foreach (Match attributeSet in m)
|
||||
{
|
||||
if (attributeSet.Groups["attributeName"].Value.ToLower() == "masterpagefile")
|
||||
{
|
||||
// validate the masterpagefile
|
||||
var currentMasterPageFile = attributeSet.Groups["attributeValue"].Value;
|
||||
var currentMasterTemplateFile = ParentTemplatePath(template);
|
||||
|
||||
if (currentMasterPageFile != currentMasterTemplateFile)
|
||||
{
|
||||
masterPageContent =
|
||||
masterPageContent.Replace(
|
||||
attributeSet.Groups["attributeName"].Value + "=\"" + currentMasterPageFile + "\"",
|
||||
attributeSet.Groups["attributeName"].Value + "=\"" + currentMasterTemplateFile +
|
||||
"\"");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return masterPageContent;
|
||||
}
|
||||
|
||||
private void UpdateChildTemplates(ITemplate template, string currentAlias, ITemplateRepository templateRepo)
|
||||
{
|
||||
//if we have a Old Alias if the alias and therefor the masterpage file name has changed...
|
||||
//so before we save the new masterfile, we'll clear the old one, so we don't up with
|
||||
//Unused masterpage files
|
||||
if (string.IsNullOrEmpty(currentAlias) == false && currentAlias != template.Alias)
|
||||
{
|
||||
//Ensure that child templates have the right master masterpage file name
|
||||
if (template.IsMasterTemplate)
|
||||
{
|
||||
var children = templateRepo.GetChildren(template.Id);
|
||||
foreach (var t in children)
|
||||
UpdateMasterPageFile(t, null, templateRepo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//private void SaveDesignToFile(ITemplate t, string currentAlias, string design)
|
||||
//{
|
||||
// //kill the old file..
|
||||
// if (string.IsNullOrEmpty(currentAlias) == false && currentAlias != t.Alias)
|
||||
// {
|
||||
// var oldFile =
|
||||
// IOHelper.MapPath(SystemDirectories.Masterpages + "/" + currentAlias.Replace(" ", "") + ".master");
|
||||
// if (System.IO.File.Exists(oldFile))
|
||||
// System.IO.File.Delete(oldFile);
|
||||
// }
|
||||
|
||||
// // save the file in UTF-8
|
||||
// System.IO.File.WriteAllText(GetFilePath(t), design, Encoding.UTF8);
|
||||
//}
|
||||
|
||||
//internal static void RemoveMasterPageFile(string alias)
|
||||
//{
|
||||
// if (string.IsNullOrWhiteSpace(alias) == false)
|
||||
// {
|
||||
// string file = IOHelper.MapPath(SystemDirectories.Masterpages + "/" + alias.Replace(" ", "") + ".master");
|
||||
// if (System.IO.File.Exists(file))
|
||||
// System.IO.File.Delete(file);
|
||||
// }
|
||||
//}
|
||||
|
||||
//internal string SaveTemplateToFile(ITemplate template, string currentAlias, ITemplateRepository templateRepo)
|
||||
//{
|
||||
// var masterPageContent = template.Content;
|
||||
// if (IsMasterPageSyntax(masterPageContent) == false)
|
||||
// masterPageContent = ConvertToMasterPageSyntax(template);
|
||||
|
||||
// // Add header to master page if it doesn't exist
|
||||
// if (masterPageContent.TrimStart().StartsWith("<%@") == false)
|
||||
// {
|
||||
// masterPageContent = GetMasterPageHeader(template) + Environment.NewLine + masterPageContent;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // verify that the masterpage attribute is the same as the masterpage
|
||||
// var masterHeader =
|
||||
// masterPageContent.Substring(0, masterPageContent.IndexOf("%>", StringComparison.Ordinal) + 2).Trim(NewLineChars);
|
||||
|
||||
// // find the masterpagefile attribute
|
||||
// var m = Regex.Matches(masterHeader, "(?<attributeName>\\S*)=\"(?<attributeValue>[^\"]*)\"",
|
||||
// RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
// foreach (Match attributeSet in m)
|
||||
// {
|
||||
// if (attributeSet.Groups["attributeName"].Value.ToLower() == "masterpagefile")
|
||||
// {
|
||||
// // validate the masterpagefile
|
||||
// var currentMasterPageFile = attributeSet.Groups["attributeValue"].Value;
|
||||
// var currentMasterTemplateFile = ParentTemplatePath(template);
|
||||
|
||||
// if (currentMasterPageFile != currentMasterTemplateFile)
|
||||
// {
|
||||
// masterPageContent =
|
||||
// masterPageContent.Replace(
|
||||
// attributeSet.Groups["attributeName"].Value + "=\"" + currentMasterPageFile + "\"",
|
||||
// attributeSet.Groups["attributeName"].Value + "=\"" + currentMasterTemplateFile +
|
||||
// "\"");
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// //we have a Old Alias if the alias and therefor the masterpage file name has changed...
|
||||
// //so before we save the new masterfile, we'll clear the old one, so we don't up with
|
||||
// //Unused masterpage files
|
||||
// if (string.IsNullOrEmpty(currentAlias) == false && currentAlias != template.Alias)
|
||||
// {
|
||||
|
||||
// //Ensure that child templates have the right master masterpage file name
|
||||
// if (template.IsMasterTemplate)
|
||||
// {
|
||||
// var children = templateRepo.GetChildren(template.Id);
|
||||
|
||||
// foreach (var t in children)
|
||||
// UpdateMasterPageFile(t, null, templateRepo);
|
||||
// }
|
||||
|
||||
// //then kill the old file..
|
||||
// var oldFile = GetFilePath(currentAlias);
|
||||
// if (_masterPageFileSystem.FileExists(oldFile))
|
||||
// _masterPageFileSystem.DeleteFile(oldFile);
|
||||
// }
|
||||
|
||||
// // save the file in UTF-8
|
||||
// System.IO.File.WriteAllText(GetFilePath(template), masterPageContent, Encoding.UTF8);
|
||||
|
||||
// return masterPageContent;
|
||||
//}
|
||||
|
||||
//internal static string ConvertToMasterPageSyntax(ITemplate template)
|
||||
//{
|
||||
// string masterPageContent = GetMasterContentElement(template) + Environment.NewLine;
|
||||
|
||||
// masterPageContent += template.Content;
|
||||
|
||||
// // Parse the design for getitems
|
||||
// masterPageContent = EnsureMasterPageSyntax(template.Alias, masterPageContent);
|
||||
|
||||
// // append ending asp:content element
|
||||
// masterPageContent += Environment.NewLine + "</asp:Content>" + Environment.NewLine;
|
||||
|
||||
// return masterPageContent;
|
||||
//}
|
||||
|
||||
public static bool IsMasterPageSyntax(string code)
|
||||
{
|
||||
return Regex.IsMatch(code, @"<%@\s*Master", RegexOptions.IgnoreCase) ||
|
||||
code.InvariantContains("<umbraco:Item") || code.InvariantContains("<asp:") || code.InvariantContains("<umbraco:Macro");
|
||||
}
|
||||
|
||||
private static string GetMasterPageHeader(ITemplate template)
|
||||
{
|
||||
return String.Format("<%@ Master Language=\"C#\" MasterPageFile=\"{0}\" AutoEventWireup=\"true\" %>", ParentTemplatePath(template)) + Environment.NewLine;
|
||||
}
|
||||
|
||||
private static string ParentTemplatePath(ITemplate template)
|
||||
{
|
||||
var masterTemplate = DefaultMasterTemplate;
|
||||
if (template.MasterTemplateAlias.IsNullOrWhiteSpace() == false)
|
||||
masterTemplate = SystemDirectories.Masterpages + "/" + template.MasterTemplateAlias + ".master";
|
||||
|
||||
return masterTemplate;
|
||||
}
|
||||
|
||||
internal static string GetMasterContentElement(ITemplate template)
|
||||
{
|
||||
if (template.MasterTemplateAlias.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
string masterAlias = template.MasterTemplateAlias;
|
||||
return
|
||||
String.Format("<asp:Content ContentPlaceHolderID=\"{0}ContentPlaceHolder\" runat=\"server\">", masterAlias);
|
||||
}
|
||||
else
|
||||
return
|
||||
String.Format("<asp:Content ContentPlaceHolderID=\"ContentPlaceHolderDefault\" runat=\"server\">");
|
||||
|
||||
}
|
||||
|
||||
internal static string EnsureMasterPageSyntax(string templateAlias, string masterPageContent)
|
||||
{
|
||||
ReplaceElement(ref masterPageContent, "?UMBRACO_GETITEM", "umbraco:Item", true);
|
||||
ReplaceElement(ref masterPageContent, "?UMBRACO_GETITEM", "umbraco:Item", false);
|
||||
|
||||
// Parse the design for macros
|
||||
ReplaceElement(ref masterPageContent, "?UMBRACO_MACRO", "umbraco:Macro", true);
|
||||
ReplaceElement(ref masterPageContent, "?UMBRACO_MACRO", "umbraco:Macro", false);
|
||||
|
||||
// Parse the design for load childs
|
||||
masterPageContent = masterPageContent.Replace("<?UMBRACO_TEMPLATE_LOAD_CHILD/>", CreateDefaultPlaceHolder(templateAlias))
|
||||
.Replace("<?UMBRACO_TEMPLATE_LOAD_CHILD />", CreateDefaultPlaceHolder(templateAlias));
|
||||
// Parse the design for aspnet forms
|
||||
GetAspNetMasterPageForm(ref masterPageContent, templateAlias);
|
||||
masterPageContent = masterPageContent.Replace("</?ASPNET_FORM>", "</form>");
|
||||
// Parse the design for aspnet heads
|
||||
masterPageContent = masterPageContent.Replace("</ASPNET_HEAD>", String.Format("<head id=\"{0}Head\" runat=\"server\">", templateAlias.Replace(" ", "")));
|
||||
masterPageContent = masterPageContent.Replace("</?ASPNET_HEAD>", "</head>");
|
||||
return masterPageContent;
|
||||
}
|
||||
|
||||
|
||||
private static void GetAspNetMasterPageForm(ref string design, string templateAlias)
|
||||
{
|
||||
var formElement = Regex.Match(design, GetElementRegExp("?ASPNET_FORM", false), RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
if (string.IsNullOrEmpty(formElement.Value) == false)
|
||||
{
|
||||
string formReplace = String.Format("<form id=\"{0}Form\" runat=\"server\">", templateAlias.Replace(" ", ""));
|
||||
if (formElement.Groups.Count == 0)
|
||||
{
|
||||
formReplace += "<asp:scriptmanager runat=\"server\"></asp:scriptmanager>";
|
||||
}
|
||||
design = design.Replace(formElement.Value, formReplace);
|
||||
}
|
||||
}
|
||||
|
||||
private static string CreateDefaultPlaceHolder(string templateAlias)
|
||||
{
|
||||
return String.Format("<asp:ContentPlaceHolder ID=\"{0}ContentPlaceHolder\" runat=\"server\"></asp:ContentPlaceHolder>", templateAlias.Replace(" ", ""));
|
||||
}
|
||||
|
||||
private static void ReplaceElement(ref string design, string elementName, string newElementName, bool checkForQuotes)
|
||||
{
|
||||
var m =
|
||||
Regex.Matches(design, GetElementRegExp(elementName, checkForQuotes),
|
||||
RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
|
||||
|
||||
foreach (Match match in m)
|
||||
{
|
||||
GroupCollection groups = match.Groups;
|
||||
|
||||
// generate new element (compensate for a closing trail on single elements ("/"))
|
||||
string elementAttributes = groups[1].Value;
|
||||
// test for macro alias
|
||||
if (elementName == "?UMBRACO_MACRO")
|
||||
{
|
||||
var tags = XmlHelper.GetAttributesFromElement(match.Value);
|
||||
if (tags["macroAlias"] != null)
|
||||
elementAttributes = String.Format(" Alias=\"{0}\"", tags["macroAlias"]) + elementAttributes;
|
||||
else if (tags["macroalias"] != null)
|
||||
elementAttributes = String.Format(" Alias=\"{0}\"", tags["macroalias"]) + elementAttributes;
|
||||
}
|
||||
string newElement = "<" + newElementName + " runat=\"server\" " + elementAttributes.Trim() + ">";
|
||||
if (elementAttributes.EndsWith("/"))
|
||||
{
|
||||
elementAttributes = elementAttributes.Substring(0, elementAttributes.Length - 1);
|
||||
}
|
||||
else if (groups[0].Value.StartsWith("</"))
|
||||
// It's a closing element, so generate that instead of a starting element
|
||||
newElement = "</" + newElementName + ">";
|
||||
|
||||
if (checkForQuotes)
|
||||
{
|
||||
// if it's inside quotes, we'll change element attribute quotes to single quotes
|
||||
newElement = newElement.Replace("\"", "'");
|
||||
newElement = String.Format("\"{0}\"", newElement);
|
||||
}
|
||||
design = design.Replace(match.Value, newElement);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetElementRegExp(string elementName, bool checkForQuotes)
|
||||
{
|
||||
if (checkForQuotes)
|
||||
return String.Format("\"<[^>\\s]*\\b{0}(\\b[^>]*)>\"", elementName);
|
||||
else
|
||||
return String.Format("<[^>\\s]*\\b{0}(\\b[^>]*)>", elementName);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,6 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public static string Install => "~/install";
|
||||
|
||||
//fixme: remove this
|
||||
[Obsolete("Master pages are obsolete and code should be removed")]
|
||||
public static string Masterpages => "~/masterpages";
|
||||
|
||||
public static string AppCode => "~/App_Code";
|
||||
|
||||
public static string AppPlugins => "~/App_Plugins";
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Trees;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
[DataContract(Name = "section", Namespace = "")]
|
||||
public class ManifestBackOfficeSection : IBackOfficeSection
|
||||
{
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models.ContentEditing;
|
||||
using Umbraco.Core.Models.Trees;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
@@ -20,7 +21,7 @@ namespace Umbraco.Core.Manifest
|
||||
{
|
||||
private static readonly string Utf8Preamble = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
|
||||
|
||||
private readonly IRuntimeCacheProvider _cache;
|
||||
private readonly IAppPolicyCache _cache;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ManifestValueValidatorCollection _validators;
|
||||
|
||||
@@ -29,16 +30,17 @@ namespace Umbraco.Core.Manifest
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManifestParser"/> class.
|
||||
/// </summary>
|
||||
public ManifestParser(IRuntimeCacheProvider cache, ManifestValueValidatorCollection validators, ILogger logger)
|
||||
: this(cache, validators, "~/App_Plugins", logger)
|
||||
public ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ILogger logger)
|
||||
: this(appCaches, validators, "~/App_Plugins", logger)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManifestParser"/> class.
|
||||
/// </summary>
|
||||
private ManifestParser(IRuntimeCacheProvider cache, ManifestValueValidatorCollection validators, string path, ILogger logger)
|
||||
private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, string path, ILogger logger)
|
||||
{
|
||||
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
|
||||
if (appCaches == null) throw new ArgumentNullException(nameof(appCaches));
|
||||
_cache = appCaches.RuntimeCache;
|
||||
_validators = validators ?? throw new ArgumentNullException(nameof(validators));
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullOrEmptyException(nameof(path));
|
||||
Path = path;
|
||||
@@ -101,6 +103,7 @@ namespace Umbraco.Core.Manifest
|
||||
var gridEditors = new List<GridEditor>();
|
||||
var contentApps = new List<ManifestContentAppDefinition>();
|
||||
var dashboards = new List<ManifestDashboardDefinition>();
|
||||
var sections = new List<ManifestBackOfficeSection>();
|
||||
|
||||
foreach (var manifest in manifests)
|
||||
{
|
||||
@@ -111,6 +114,7 @@ namespace Umbraco.Core.Manifest
|
||||
if (manifest.GridEditors != null) gridEditors.AddRange(manifest.GridEditors);
|
||||
if (manifest.ContentApps != null) contentApps.AddRange(manifest.ContentApps);
|
||||
if (manifest.Dashboards != null) dashboards.AddRange(manifest.Dashboards);
|
||||
if (manifest.Sections != null) sections.AddRange(manifest.Sections.DistinctBy(x => x.Alias.ToLowerInvariant()));
|
||||
}
|
||||
|
||||
return new PackageManifest
|
||||
@@ -121,7 +125,8 @@ namespace Umbraco.Core.Manifest
|
||||
ParameterEditors = parameterEditors.ToArray(),
|
||||
GridEditors = gridEditors.ToArray(),
|
||||
ContentApps = contentApps.ToArray(),
|
||||
Dashboards = dashboards.ToArray()
|
||||
Dashboards = dashboards.ToArray(),
|
||||
Sections = sections.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Models.ContentEditing;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
@@ -10,25 +9,52 @@ namespace Umbraco.Core.Manifest
|
||||
/// </summary>
|
||||
public class PackageManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the scripts listed in the manifest.
|
||||
/// </summary>
|
||||
[JsonProperty("javascript")]
|
||||
public string[] Scripts { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stylesheets listed in the manifest.
|
||||
/// </summary>
|
||||
[JsonProperty("css")]
|
||||
public string[] Stylesheets { get; set; }= Array.Empty<string>();
|
||||
public string[] Stylesheets { get; set; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the property editors listed in the manifest.
|
||||
/// </summary>
|
||||
[JsonProperty("propertyEditors")]
|
||||
public IDataEditor[] PropertyEditors { get; set; } = Array.Empty<IDataEditor>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parameter editors listed in the manifest.
|
||||
/// </summary>
|
||||
[JsonProperty("parameterEditors")]
|
||||
public IDataEditor[] ParameterEditors { get; set; } = Array.Empty<IDataEditor>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the grid editors listed in the manifest.
|
||||
/// </summary>
|
||||
[JsonProperty("gridEditors")]
|
||||
public GridEditor[] GridEditors { get; set; } = Array.Empty<GridEditor>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the content apps listed in the manifest.
|
||||
/// </summary>
|
||||
[JsonProperty("contentApps")]
|
||||
public ManifestContentAppDefinition[] ContentApps { get; set; } = Array.Empty<ManifestContentAppDefinition>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dashboards listed in the manifest.
|
||||
/// </summary>
|
||||
[JsonProperty("dashboards")]
|
||||
public ManifestDashboardDefinition[] Dashboards { get; set; } = Array.Empty<ManifestDashboardDefinition>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sections listed in the manifest.
|
||||
/// </summary>
|
||||
[JsonProperty("sections")]
|
||||
public ManifestBackOfficeSection[] Sections { get; set; } = Array.Empty<ManifestBackOfficeSection>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
[DebuggerDisplay("Tree - {Title} ({ApplicationAlias})")]
|
||||
public class ApplicationTree
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, Type> ResolvedTypes = new ConcurrentDictionary<string, Type>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationTree"/> class.
|
||||
/// </summary>
|
||||
public ApplicationTree() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApplicationTree"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialize">if set to <c>true</c> [initialize].</param>
|
||||
/// <param name="sortOrder">The sort order.</param>
|
||||
/// <param name="applicationAlias">The application alias.</param>
|
||||
/// <param name="alias">The tree alias.</param>
|
||||
/// <param name="title">The tree title.</param>
|
||||
/// <param name="iconClosed">The icon closed.</param>
|
||||
/// <param name="iconOpened">The icon opened.</param>
|
||||
/// <param name="type">The tree type.</param>
|
||||
public ApplicationTree(bool initialize, int sortOrder, string applicationAlias, string alias, string title, string iconClosed, string iconOpened, string type)
|
||||
{
|
||||
Initialize = initialize;
|
||||
SortOrder = sortOrder;
|
||||
ApplicationAlias = applicationAlias;
|
||||
Alias = alias;
|
||||
Title = title;
|
||||
IconClosed = iconClosed;
|
||||
IconOpened = iconOpened;
|
||||
Type = type;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="ApplicationTree"/> should initialize.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if initialize; otherwise, <c>false</c>.</value>
|
||||
public bool Initialize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sort order.
|
||||
/// </summary>
|
||||
/// <value>The sort order.</value>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the application alias.
|
||||
/// </summary>
|
||||
/// <value>The application alias.</value>
|
||||
public string ApplicationAlias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tree alias.
|
||||
/// </summary>
|
||||
/// <value>The alias.</value>
|
||||
public string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tree title.
|
||||
/// </summary>
|
||||
/// <value>The title.</value>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon closed.
|
||||
/// </summary>
|
||||
/// <value>The icon closed.</value>
|
||||
public string IconClosed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon opened.
|
||||
/// </summary>
|
||||
/// <value>The icon opened.</value>
|
||||
public string IconOpened { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the tree type assembly name.
|
||||
/// </summary>
|
||||
/// <value>The type.</value>
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the localized root node display name
|
||||
/// </summary>
|
||||
/// <param name="textService"></param>
|
||||
/// <returns></returns>
|
||||
public string GetRootNodeDisplayName(ILocalizedTextService textService)
|
||||
{
|
||||
var label = $"[{Alias}]";
|
||||
|
||||
// try to look up a the localized tree header matching the tree alias
|
||||
var localizedLabel = textService.Localize("treeHeaders/" + Alias);
|
||||
|
||||
// if the localizedLabel returns [alias] then return the title attribute from the trees.config file, if it's defined
|
||||
if (localizedLabel != null && localizedLabel.Equals(label, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
if (string.IsNullOrEmpty(Title) == false)
|
||||
label = Title;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the localizedLabel translated into something that's not just [alias], so use the translation
|
||||
label = localizedLabel;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
private Type _runtimeType;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the CLR type based on it's assembly name stored in the config
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Type GetRuntimeType()
|
||||
{
|
||||
return _runtimeType ?? (_runtimeType = System.Type.GetType(Type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to try to get and cache the tree type
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
internal static Type TryGetType(string type)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ResolvedTypes.GetOrAdd(type, s =>
|
||||
{
|
||||
var result = System.Type.GetType(type);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//we need to implement a bit of a hack here due to some trees being renamed and backwards compat
|
||||
var parts = type.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length != 2)
|
||||
throw new InvalidOperationException("Could not resolve type");
|
||||
if (parts[1].Trim() != "Umbraco.Web" || parts[0].StartsWith("Umbraco.Web.Trees") == false || parts[0].EndsWith("Controller"))
|
||||
throw new InvalidOperationException("Could not resolve type");
|
||||
|
||||
//if it's one of our controllers but it's not suffixed with "Controller" then add it and try again
|
||||
var tempType = parts[0] + "Controller, Umbraco.Web";
|
||||
|
||||
result = System.Type.GetType(tempType);
|
||||
if (result != null)
|
||||
return result;
|
||||
|
||||
throw new InvalidOperationException("Could not resolve type");
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
//swallow, this is our own exception, couldn't find the type
|
||||
// fixme bad use of exceptions here!
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Template File (Masterpage or Mvc View)
|
||||
/// Defines a Template File (Mvc View)
|
||||
/// </summary>
|
||||
public interface ITemplate : IFile, IRememberBeingDirty, ICanBeDirty
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Models.Packaging
|
||||
public string Author { get; set; }
|
||||
public string AuthorUrl { get; set; }
|
||||
public string Readme { get; set; }
|
||||
public string Control { get; set; }
|
||||
public string PackageView { get; set; }
|
||||
public string IconUrl { get; set; }
|
||||
|
||||
public string Actions { get; set; } //fixme: Should we make this strongly typed to IEnumerable<PackageAction> ?
|
||||
|
||||
@@ -13,7 +13,12 @@ namespace Umbraco.Core.Models.Packaging
|
||||
string Author { get; }
|
||||
string AuthorUrl { get; }
|
||||
string Readme { get; }
|
||||
string Control { get; } //fixme - this needs to be an angular view
|
||||
|
||||
/// <summary>
|
||||
/// This is the angular view path that will be loaded when the package installs
|
||||
/// </summary>
|
||||
string PackageView { get; }
|
||||
|
||||
string IconUrl { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Models.Packaging
|
||||
Actions = compiled.Actions,
|
||||
Author = compiled.Author,
|
||||
AuthorUrl = compiled.AuthorUrl,
|
||||
Control = compiled.Control,
|
||||
PackageView = compiled.PackageView,
|
||||
IconUrl = compiled.IconUrl,
|
||||
License = compiled.License,
|
||||
LicenseUrl = compiled.LicenseUrl,
|
||||
@@ -115,9 +115,9 @@ namespace Umbraco.Core.Models.Packaging
|
||||
[DataMember(Name = "files")]
|
||||
public IList<string> Files { get; set; } = new List<string>();
|
||||
|
||||
//fixme: Change this to angular view
|
||||
[DataMember(Name = "loadControl")]
|
||||
public string Control { get; set; } = string.Empty;
|
||||
/// <inheritdoc />
|
||||
[DataMember(Name = "packageView")]
|
||||
public string PackageView { get; set; } = string.Empty;
|
||||
|
||||
[DataMember(Name = "actions")]
|
||||
public string Actions { get; set; } = "<actions></actions>";
|
||||
@@ -127,38 +127,7 @@ namespace Umbraco.Core.Models.Packaging
|
||||
|
||||
[DataMember(Name = "iconUrl")]
|
||||
public string IconUrl { get; set; } = string.Empty;
|
||||
|
||||
public PackageDefinition Clone()
|
||||
{
|
||||
return new PackageDefinition
|
||||
{
|
||||
Id = Id,
|
||||
PackagePath = PackagePath,
|
||||
Name = Name,
|
||||
Files = new List<string>(Files),
|
||||
UmbracoVersion = (Version) UmbracoVersion.Clone(),
|
||||
Version = Version,
|
||||
Url = Url,
|
||||
Readme = Readme,
|
||||
AuthorUrl = AuthorUrl,
|
||||
Author = Author,
|
||||
LicenseUrl = LicenseUrl,
|
||||
Actions = Actions,
|
||||
PackageId = PackageId,
|
||||
Control = Control,
|
||||
DataTypes = new List<string>(DataTypes),
|
||||
IconUrl = IconUrl,
|
||||
License = License,
|
||||
Templates = new List<string>(Templates),
|
||||
Languages = new List<string>(Languages),
|
||||
Macros = new List<string>(Macros),
|
||||
Stylesheets = new List<string>(Stylesheets),
|
||||
DocumentTypes = new List<string>(DocumentTypes),
|
||||
DictionaryItems = new List<string>(DictionaryItems),
|
||||
ContentNodeId = ContentNodeId,
|
||||
ContentLoadChildNodes = ContentLoadChildNodes
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a section defined in the app.config file.
|
||||
/// </summary>
|
||||
public class Section
|
||||
{
|
||||
public Section(string name, string @alias, int sortOrder)
|
||||
{
|
||||
Name = name;
|
||||
Alias = alias;
|
||||
SortOrder = sortOrder;
|
||||
}
|
||||
|
||||
public Section()
|
||||
{ }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Alias { get; set; }
|
||||
public int SortOrder { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Umbraco.Core.Models.Trees
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a back office section.
|
||||
/// </summary>
|
||||
public interface IBackOfficeSection
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the alias of the section.
|
||||
/// </summary>
|
||||
string Alias { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the section.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
@@ -50,11 +50,11 @@ namespace Umbraco.Core.Models
|
||||
/// Tries to lookup the user's gravatar to see if the endpoint can be reached, if so it returns the valid URL
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="staticCache"></param>
|
||||
/// <param name="cache"></param>
|
||||
/// <returns>
|
||||
/// A list of 5 different sized avatar URLs
|
||||
/// </returns>
|
||||
internal static string[] GetUserAvatarUrls(this IUser user, ICacheProvider staticCache)
|
||||
internal static string[] GetUserAvatarUrls(this IUser user, IAppCache cache)
|
||||
{
|
||||
// If FIPS is required, never check the Gravatar service as it only supports MD5 hashing.
|
||||
// Unfortunately, if the FIPS setting is enabled on Windows, using MD5 will throw an exception
|
||||
@@ -71,7 +71,7 @@ namespace Umbraco.Core.Models
|
||||
var gravatarUrl = "https://www.gravatar.com/avatar/" + gravatarHash + "?d=404";
|
||||
|
||||
//try gravatar
|
||||
var gravatarAccess = staticCache.GetCacheItem<bool>("UserAvatar" + user.Id, () =>
|
||||
var gravatarAccess = cache.GetCacheItem<bool>("UserAvatar" + user.Id, () =>
|
||||
{
|
||||
// Test if we can reach this URL, will fail when there's network or firewall errors
|
||||
var request = (HttpWebRequest)WebRequest.Create(gravatarUrl);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Packaging
|
||||
IconUrl = package.Element("iconUrl")?.Value,
|
||||
UmbracoVersion = new Version((int)requirements.Element("major"), (int)requirements.Element("minor"), (int)requirements.Element("patch")),
|
||||
UmbracoVersionRequirementsType = requirements.AttributeValue<string>("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum<RequirementsType>.Parse(requirements.AttributeValue<string>("type"), true),
|
||||
Control = package.Element("control")?.Value,
|
||||
PackageView = xml.Root.Element("view")?.Value,
|
||||
Actions = xml.Root.Element("Actions")?.ToString(SaveOptions.None) ?? "<Actions></Actions>", //take the entire outer xml value
|
||||
Files = xml.Root.Element("files")?.Elements("file")?.Select(CompiledPackageFile.Create).ToList() ?? new List<CompiledPackageFile>(),
|
||||
Macros = xml.Root.Element("Macros")?.Elements("macro") ?? Enumerable.Empty<XElement>(),
|
||||
|
||||
@@ -1234,10 +1234,7 @@ namespace Umbraco.Core.Packaging
|
||||
var alias = templateElement.Element("Alias").Value;
|
||||
var design = templateElement.Element("Design").Value;
|
||||
var masterElement = templateElement.Element("Master");
|
||||
|
||||
var isMasterPage = IsMasterPageSyntax(design);
|
||||
var path = isMasterPage ? MasterpagePath(alias) : ViewPath(alias);
|
||||
|
||||
|
||||
var existingTemplate = _fileService.GetTemplate(alias) as Template;
|
||||
var template = existingTemplate ?? new Template(templateName, alias);
|
||||
template.Content = design;
|
||||
@@ -1257,23 +1254,11 @@ namespace Umbraco.Core.Packaging
|
||||
return templates;
|
||||
}
|
||||
|
||||
|
||||
private bool IsMasterPageSyntax(string code)
|
||||
{
|
||||
return Regex.IsMatch(code, @"<%@\s*Master", RegexOptions.IgnoreCase) ||
|
||||
code.InvariantContains("<umbraco:Item") || code.InvariantContains("<asp:") || code.InvariantContains("<umbraco:Macro");
|
||||
}
|
||||
|
||||
private string ViewPath(string alias)
|
||||
{
|
||||
return SystemDirectories.MvcViews + "/" + alias.Replace(" ", "") + ".cshtml";
|
||||
}
|
||||
|
||||
private string MasterpagePath(string alias)
|
||||
{
|
||||
return IOHelper.MapPath(SystemDirectories.Masterpages + "/" + alias.Replace(" ", "") + ".master");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Umbraco.Core.Packaging
|
||||
PackageId = xml.AttributeValue<Guid>("packageGuid"),
|
||||
IconUrl = xml.AttributeValue<string>("iconUrl") ?? string.Empty,
|
||||
UmbracoVersion = xml.AttributeValue<Version>("umbVersion"),
|
||||
PackageView = xml.AttributeValue<string>("view") ?? string.Empty,
|
||||
License = xml.Element("license")?.Value ?? string.Empty,
|
||||
LicenseUrl = xml.Element("license")?.AttributeValue<string>("url") ?? string.Empty,
|
||||
Author = xml.Element("author")?.Value ?? string.Empty,
|
||||
@@ -49,8 +50,7 @@ namespace Umbraco.Core.Packaging
|
||||
Languages = xml.Element("languages")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
|
||||
DictionaryItems = xml.Element("dictionaryitems")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
|
||||
DataTypes = xml.Element("datatypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
|
||||
Files = xml.Element("files")?.Elements("file").Select(x => x.Value).ToList() ?? new List<string>(),
|
||||
Control = xml.Element("loadcontrol")?.Value ?? string.Empty
|
||||
Files = xml.Element("files")?.Elements("file").Select(x => x.Value).ToList() ?? new List<string>()
|
||||
};
|
||||
|
||||
return retVal;
|
||||
@@ -77,6 +77,7 @@ namespace Umbraco.Core.Packaging
|
||||
new XAttribute("iconUrl", def.IconUrl ?? string.Empty),
|
||||
new XAttribute("umbVersion", def.UmbracoVersion),
|
||||
new XAttribute("packageGuid", def.PackageId),
|
||||
new XAttribute("view", def.PackageView ?? string.Empty),
|
||||
|
||||
new XElement("license",
|
||||
new XCData(def.License ?? string.Empty),
|
||||
@@ -100,8 +101,7 @@ namespace Umbraco.Core.Packaging
|
||||
new XElement("macros", string.Join(",", def.Macros ?? Array.Empty<string>())),
|
||||
new XElement("files", (def.Files ?? Array.Empty<string>()).Where(x => !x.IsNullOrWhiteSpace()).Select(x => new XElement("file", x))),
|
||||
new XElement("languages", string.Join(",", def.Languages ?? Array.Empty<string>())),
|
||||
new XElement("dictionaryitems", string.Join(",", def.DictionaryItems ?? Array.Empty<string>())),
|
||||
new XElement("loadcontrol", def.Control ?? string.Empty)); //fixme: no more loadcontrol, needs to be an angular view
|
||||
new XElement("dictionaryitems", string.Join(",", def.DictionaryItems ?? Array.Empty<string>())));
|
||||
|
||||
return packageXml;
|
||||
}
|
||||
|
||||
@@ -161,30 +161,30 @@ namespace Umbraco.Core.Packaging
|
||||
try
|
||||
{
|
||||
//Init package file
|
||||
var packageManifest = CreatePackageManifest(out var manifestRoot, out var filesXml);
|
||||
var compiledPackageXml = CreateCompiledPackageXml(out var root, out var filesXml);
|
||||
|
||||
//Info section
|
||||
manifestRoot.Add(GetPackageInfoXml(definition));
|
||||
root.Add(GetPackageInfoXml(definition));
|
||||
|
||||
PackageDocumentsAndTags(definition, manifestRoot);
|
||||
PackageDocumentTypes(definition, manifestRoot);
|
||||
PackageTemplates(definition, manifestRoot);
|
||||
PackageStylesheets(definition, manifestRoot);
|
||||
PackageMacros(definition, manifestRoot, filesXml, temporaryPath);
|
||||
PackageDictionaryItems(definition, manifestRoot);
|
||||
PackageLanguages(definition, manifestRoot);
|
||||
PackageDataTypes(definition, manifestRoot);
|
||||
PackageDocumentsAndTags(definition, root);
|
||||
PackageDocumentTypes(definition, root);
|
||||
PackageTemplates(definition, root);
|
||||
PackageStylesheets(definition, root);
|
||||
PackageMacros(definition, root, filesXml, temporaryPath);
|
||||
PackageDictionaryItems(definition, root);
|
||||
PackageLanguages(definition, root);
|
||||
PackageDataTypes(definition, root);
|
||||
|
||||
//Files
|
||||
foreach (var fileName in definition.Files)
|
||||
AppendFileToManifest(fileName, temporaryPath, filesXml);
|
||||
AppendFileToPackage(fileName, temporaryPath, filesXml);
|
||||
|
||||
//Load control on install...
|
||||
if (!string.IsNullOrEmpty(definition.Control))
|
||||
//Load view on install...
|
||||
if (!string.IsNullOrEmpty(definition.PackageView))
|
||||
{
|
||||
var control = new XElement("control", definition.Control);
|
||||
AppendFileToManifest(definition.Control, temporaryPath, filesXml);
|
||||
manifestRoot.Add(control);
|
||||
var control = new XElement("view", definition.PackageView);
|
||||
AppendFileToPackage(definition.PackageView, temporaryPath, filesXml);
|
||||
root.Add(control);
|
||||
}
|
||||
|
||||
//Actions
|
||||
@@ -196,20 +196,20 @@ namespace Umbraco.Core.Packaging
|
||||
//this will be formatted like a full xml block like <actions>...</actions> and we want the child nodes
|
||||
var parsed = XElement.Parse(definition.Actions);
|
||||
actionsXml.Add(parsed.Elements());
|
||||
manifestRoot.Add(actionsXml);
|
||||
root.Add(actionsXml);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Warn<PackagesRepository>(e, "Could not add package actions to the package manifest, the xml did not parse");
|
||||
_logger.Warn<PackagesRepository>(e, "Could not add package actions to the package, the xml did not parse");
|
||||
}
|
||||
}
|
||||
|
||||
var manifestFileName = temporaryPath + "/package.xml";
|
||||
var packageXmlFileName = temporaryPath + "/package.xml";
|
||||
|
||||
if (File.Exists(manifestFileName))
|
||||
File.Delete(manifestFileName);
|
||||
if (File.Exists(packageXmlFileName))
|
||||
File.Delete(packageXmlFileName);
|
||||
|
||||
packageManifest.Save(manifestFileName);
|
||||
compiledPackageXml.Save(packageXmlFileName);
|
||||
|
||||
// check if there's a packages directory below media
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace Umbraco.Core.Packaging
|
||||
throw new InvalidOperationException("Validation failed, there is invalid data on the model: " + string.Join(", ", results.Select(x => x.ErrorMessage)));
|
||||
}
|
||||
|
||||
private void PackageDataTypes(PackageDefinition definition, XContainer manifestRoot)
|
||||
private void PackageDataTypes(PackageDefinition definition, XContainer root)
|
||||
{
|
||||
var dataTypes = new XElement("DataTypes");
|
||||
foreach (var dtId in definition.DataTypes)
|
||||
@@ -252,10 +252,10 @@ namespace Umbraco.Core.Packaging
|
||||
if (dataType == null) continue;
|
||||
dataTypes.Add(_serializer.Serialize(dataType));
|
||||
}
|
||||
manifestRoot.Add(dataTypes);
|
||||
root.Add(dataTypes);
|
||||
}
|
||||
|
||||
private void PackageLanguages(PackageDefinition definition, XContainer manifestRoot)
|
||||
private void PackageLanguages(PackageDefinition definition, XContainer root)
|
||||
{
|
||||
var languages = new XElement("Languages");
|
||||
foreach (var langId in definition.Languages)
|
||||
@@ -265,10 +265,10 @@ namespace Umbraco.Core.Packaging
|
||||
if (lang == null) continue;
|
||||
languages.Add(_serializer.Serialize(lang));
|
||||
}
|
||||
manifestRoot.Add(languages);
|
||||
root.Add(languages);
|
||||
}
|
||||
|
||||
private void PackageDictionaryItems(PackageDefinition definition, XContainer manifestRoot)
|
||||
private void PackageDictionaryItems(PackageDefinition definition, XContainer root)
|
||||
{
|
||||
var dictionaryItems = new XElement("DictionaryItems");
|
||||
foreach (var dictionaryId in definition.DictionaryItems)
|
||||
@@ -278,10 +278,10 @@ namespace Umbraco.Core.Packaging
|
||||
if (di == null) continue;
|
||||
dictionaryItems.Add(_serializer.Serialize(di, false));
|
||||
}
|
||||
manifestRoot.Add(dictionaryItems);
|
||||
root.Add(dictionaryItems);
|
||||
}
|
||||
|
||||
private void PackageMacros(PackageDefinition definition, XContainer manifestRoot, XContainer filesXml, string temporaryPath)
|
||||
private void PackageMacros(PackageDefinition definition, XContainer root, XContainer filesXml, string temporaryPath)
|
||||
{
|
||||
var macros = new XElement("Macros");
|
||||
foreach (var macroId in definition.Macros)
|
||||
@@ -291,14 +291,14 @@ namespace Umbraco.Core.Packaging
|
||||
var macroXml = GetMacroXml(outInt, out var macro);
|
||||
if (macroXml == null) continue;
|
||||
macros.Add(macroXml);
|
||||
//if the macro has a file copy it to the manifest
|
||||
//if the macro has a file copy it to the xml
|
||||
if (!string.IsNullOrEmpty(macro.MacroSource))
|
||||
AppendFileToManifest(macro.MacroSource, temporaryPath, filesXml);
|
||||
AppendFileToPackage(macro.MacroSource, temporaryPath, filesXml);
|
||||
}
|
||||
manifestRoot.Add(macros);
|
||||
root.Add(macros);
|
||||
}
|
||||
|
||||
private void PackageStylesheets(PackageDefinition definition, XContainer manifestRoot)
|
||||
private void PackageStylesheets(PackageDefinition definition, XContainer root)
|
||||
{
|
||||
var stylesheetsXml = new XElement("Stylesheets");
|
||||
foreach (var stylesheetName in definition.Stylesheets)
|
||||
@@ -308,10 +308,10 @@ namespace Umbraco.Core.Packaging
|
||||
if (xml != null)
|
||||
stylesheetsXml.Add(xml);
|
||||
}
|
||||
manifestRoot.Add(stylesheetsXml);
|
||||
root.Add(stylesheetsXml);
|
||||
}
|
||||
|
||||
private void PackageTemplates(PackageDefinition definition, XContainer manifestRoot)
|
||||
private void PackageTemplates(PackageDefinition definition, XContainer root)
|
||||
{
|
||||
var templatesXml = new XElement("Templates");
|
||||
foreach (var templateId in definition.Templates)
|
||||
@@ -321,10 +321,10 @@ namespace Umbraco.Core.Packaging
|
||||
if (template == null) continue;
|
||||
templatesXml.Add(_serializer.Serialize(template));
|
||||
}
|
||||
manifestRoot.Add(templatesXml);
|
||||
root.Add(templatesXml);
|
||||
}
|
||||
|
||||
private void PackageDocumentTypes(PackageDefinition definition, XContainer manifestRoot)
|
||||
private void PackageDocumentTypes(PackageDefinition definition, XContainer root)
|
||||
{
|
||||
var contentTypes = new HashSet<IContentType>();
|
||||
var docTypesXml = new XElement("DocumentTypes");
|
||||
@@ -338,10 +338,10 @@ namespace Umbraco.Core.Packaging
|
||||
foreach (var contentType in contentTypes)
|
||||
docTypesXml.Add(_serializer.Serialize(contentType));
|
||||
|
||||
manifestRoot.Add(docTypesXml);
|
||||
root.Add(docTypesXml);
|
||||
}
|
||||
|
||||
private void PackageDocumentsAndTags(PackageDefinition definition, XContainer manifestRoot)
|
||||
private void PackageDocumentsAndTags(PackageDefinition definition, XContainer root)
|
||||
{
|
||||
//Documents and tags
|
||||
if (string.IsNullOrEmpty(definition.ContentNodeId) == false && int.TryParse(definition.ContentNodeId, out var contentNodeId))
|
||||
@@ -356,7 +356,7 @@ namespace Umbraco.Core.Packaging
|
||||
|
||||
//Create the Documents/DocumentSet node
|
||||
|
||||
manifestRoot.Add(
|
||||
root.Add(
|
||||
new XElement("Documents",
|
||||
new XElement("DocumentSet",
|
||||
new XAttribute("importMode", "root"),
|
||||
@@ -438,12 +438,12 @@ namespace Umbraco.Core.Packaging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a file to package manifest and copies the file to the correct folder.
|
||||
/// Appends a file to package and copies the file to the correct folder.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="packageDirectory">The package directory.</param>
|
||||
/// <param name="filesXml">The files xml node</param>
|
||||
private static void AppendFileToManifest(string path, string packageDirectory, XContainer filesXml)
|
||||
private static void AppendFileToPackage(string path, string packageDirectory, XContainer filesXml)
|
||||
{
|
||||
if (!path.StartsWith("~/") && !path.StartsWith("/"))
|
||||
path = "~/" + path;
|
||||
@@ -584,12 +584,12 @@ namespace Umbraco.Core.Packaging
|
||||
return info;
|
||||
}
|
||||
|
||||
private static XDocument CreatePackageManifest(out XElement root, out XElement files)
|
||||
private static XDocument CreateCompiledPackageXml(out XElement root, out XElement files)
|
||||
{
|
||||
files = new XElement("files");
|
||||
root = new XElement("umbPackage", files);
|
||||
var packageManifest = new XDocument(root);
|
||||
return packageManifest;
|
||||
var compiledPackageXml = new XDocument(root);
|
||||
return compiledPackageXml;
|
||||
}
|
||||
|
||||
private XDocument EnsureStorage(out string packagesFile)
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
@@ -18,21 +16,6 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
IEnumerable<ITemplate> GetDescendants(int masterTemplateId);
|
||||
IEnumerable<ITemplate> GetDescendants(string alias);
|
||||
|
||||
/// <summary>
|
||||
/// This checks what the default rendering engine is set in config but then also ensures that there isn't already
|
||||
/// a template that exists in the opposite rendering engine's template folder, then returns the appropriate
|
||||
/// rendering engine to use.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// The reason this is required is because for example, if you have a master page file already existing under ~/masterpages/Blah.aspx
|
||||
/// and then you go to create a template in the tree called Blah and the default rendering engine is MVC, it will create a Blah.cshtml
|
||||
/// empty template in ~/Views. This means every page that is using Blah will go to MVC and render an empty page.
|
||||
/// This is mostly related to installing packages since packages install file templates to the file system and then create the
|
||||
/// templates in business logic. Without this, it could cause the wrong rendering engine to be used for a package.
|
||||
/// </remarks>
|
||||
RenderingEngine DetermineTemplateRenderingEngine(ITemplate template);
|
||||
|
||||
/// <summary>
|
||||
/// Validates a <see cref="ITemplate"/>
|
||||
/// </summary>
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuditEntryRepository"/> class.
|
||||
/// </summary>
|
||||
public AuditEntryRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public AuditEntryRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
internal class AuditRepository : NPocoRepositoryBase<int, IAuditItem>, IAuditRepository
|
||||
{
|
||||
public AuditRepository(IScopeAccessor scopeAccessor, ILogger logger)
|
||||
: base(scopeAccessor, CacheHelper.NoCache, logger)
|
||||
: base(scopeAccessor, AppCaches.NoCache, logger)
|
||||
{ }
|
||||
|
||||
protected override void PersistNewItem(IAuditItem entity)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConsentRepository"/> class.
|
||||
/// </summary>
|
||||
public ConsentRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public ConsentRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
Database.Update(dto);
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IConsent>(entity.Id));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IConsent>(entity.Id));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
where TEntity : class, IUmbracoEntity
|
||||
where TRepository : class, IRepository
|
||||
{
|
||||
protected ContentRepositoryBase(IScopeAccessor scopeAccessor, CacheHelper cache, ILanguageRepository languageRepository, ILogger logger)
|
||||
protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILanguageRepository languageRepository, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
LanguageRepository = languageRepository;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
private readonly ITemplateRepository _templateRepository;
|
||||
|
||||
public ContentTypeRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, ITemplateRepository templateRepository)
|
||||
public ContentTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, ITemplateRepository templateRepository)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
_templateRepository = templateRepository;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
internal abstract class ContentTypeRepositoryBase<TEntity> : NPocoRepositoryBase<int, TEntity>, IReadRepository<Guid, TEntity>
|
||||
where TEntity : class, IContentTypeComposition
|
||||
{
|
||||
protected ContentTypeRepositoryBase(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
protected ContentTypeRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
class DataTypeContainerRepository : EntityContainerRepository, IDataTypeContainerRepository
|
||||
{
|
||||
public DataTypeContainerRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public DataTypeContainerRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger, Constants.ObjectTypes.DataTypeContainer)
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly Lazy<PropertyEditorCollection> _editors;
|
||||
|
||||
// fixme temp fixing circular dependencies with LAZY but is this the right place?
|
||||
public DataTypeRepository(IScopeAccessor scopeAccessor, CacheHelper cache, Lazy<PropertyEditorCollection> editors, ILogger logger)
|
||||
public DataTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, Lazy<PropertyEditorCollection> editors, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
_editors = editors;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
internal class DictionaryRepository : NPocoRepositoryBase<int, IDictionaryItem>, IDictionaryRepository
|
||||
{
|
||||
public DictionaryRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public DictionaryRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
@@ -174,8 +174,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
entity.ResetDirtyProperties();
|
||||
|
||||
//Clear the cache entries that exist by uniqueid/item key
|
||||
IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.ItemKey));
|
||||
IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.Key));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.ItemKey));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.Key));
|
||||
}
|
||||
|
||||
protected override void PersistDeletedItem(IDictionaryItem entity)
|
||||
@@ -186,8 +186,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
Database.Delete<DictionaryDto>("WHERE id = @Id", new { Id = entity.Key });
|
||||
|
||||
//Clear the cache entries that exist by uniqueid/item key
|
||||
IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.ItemKey));
|
||||
IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.Key));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.ItemKey));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(entity.Key));
|
||||
|
||||
entity.DeleteDate = DateTime.Now;
|
||||
}
|
||||
@@ -203,8 +203,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
Database.Delete<DictionaryDto>("WHERE id = @Id", new { Id = dto.UniqueId });
|
||||
|
||||
//Clear the cache entries that exist by uniqueid/item key
|
||||
IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IDictionaryItem>(dto.Key));
|
||||
IsolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IDictionaryItem>(dto.UniqueId));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(dto.Key));
|
||||
IsolatedCache.Clear(RepositoryCacheKeys.GetKey<IDictionaryItem>(dto.UniqueId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,13 +224,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
public IDictionaryItem Get(Guid uniqueId)
|
||||
{
|
||||
var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, ScopeAccessor, GlobalCache, Logger);
|
||||
var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, ScopeAccessor, AppCaches, Logger);
|
||||
return uniqueIdRepo.Get(uniqueId);
|
||||
}
|
||||
|
||||
public IDictionaryItem Get(string key)
|
||||
{
|
||||
var keyRepo = new DictionaryByKeyRepository(this, ScopeAccessor, GlobalCache, Logger);
|
||||
var keyRepo = new DictionaryByKeyRepository(this, ScopeAccessor, AppCaches, Logger);
|
||||
return keyRepo.Get(key);
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
private readonly DictionaryRepository _dictionaryRepository;
|
||||
|
||||
public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
_dictionaryRepository = dictionaryRepository;
|
||||
@@ -343,7 +343,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
private readonly DictionaryRepository _dictionaryRepository;
|
||||
|
||||
public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
_dictionaryRepository = dictionaryRepository;
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </remarks>
|
||||
internal class DocumentBlueprintRepository : DocumentRepository, IDocumentBlueprintRepository
|
||||
{
|
||||
public DocumentBlueprintRepository(IScopeAccessor scopeAccessor, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IContentSection settings)
|
||||
: base(scopeAccessor, cacheHelper, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, settings)
|
||||
public DocumentBlueprintRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IContentSection settings)
|
||||
: base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, settings)
|
||||
{
|
||||
EnsureUniqueNaming = false; // duplicates are allowed
|
||||
}
|
||||
|
||||
@@ -25,20 +25,20 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly IContentTypeRepository _contentTypeRepository;
|
||||
private readonly ITemplateRepository _templateRepository;
|
||||
private readonly ITagRepository _tagRepository;
|
||||
private readonly CacheHelper _cacheHelper;
|
||||
private readonly AppCaches _appCaches;
|
||||
private PermissionRepository<IContent> _permissionRepository;
|
||||
private readonly ContentByGuidReadRepository _contentByGuidReadRepository;
|
||||
private readonly IScopeAccessor _scopeAccessor;
|
||||
|
||||
public DocumentRepository(IScopeAccessor scopeAccessor, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IContentSection settings)
|
||||
: base(scopeAccessor, cacheHelper, languageRepository, logger)
|
||||
public DocumentRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IContentSection settings)
|
||||
: base(scopeAccessor, appCaches, languageRepository, logger)
|
||||
{
|
||||
_contentTypeRepository = contentTypeRepository ?? throw new ArgumentNullException(nameof(contentTypeRepository));
|
||||
_templateRepository = templateRepository ?? throw new ArgumentNullException(nameof(templateRepository));
|
||||
_tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository));
|
||||
_cacheHelper = cacheHelper;
|
||||
_appCaches = appCaches;
|
||||
_scopeAccessor = scopeAccessor;
|
||||
_contentByGuidReadRepository = new ContentByGuidReadRepository(this, scopeAccessor, cacheHelper, logger);
|
||||
_contentByGuidReadRepository = new ContentByGuidReadRepository(this, scopeAccessor, appCaches, logger);
|
||||
EnsureUniqueNaming = settings.EnsureUniqueNaming;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
// note: is ok to 'new' the repo here as it's a sub-repo really
|
||||
private PermissionRepository<IContent> PermissionRepository => _permissionRepository
|
||||
?? (_permissionRepository = new PermissionRepository<IContent>(_scopeAccessor, _cacheHelper, Logger));
|
||||
?? (_permissionRepository = new PermissionRepository<IContent>(_scopeAccessor, _appCaches, Logger));
|
||||
|
||||
#region Repository Base
|
||||
|
||||
@@ -847,7 +847,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
private readonly DocumentRepository _outerRepo;
|
||||
|
||||
public ContentByGuidReadRepository(DocumentRepository outerRepo, IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public ContentByGuidReadRepository(DocumentRepository outerRepo, IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
_outerRepo = outerRepo;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
class DocumentTypeContainerRepository : EntityContainerRepository, IDocumentTypeContainerRepository
|
||||
{
|
||||
public DocumentTypeContainerRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public DocumentTypeContainerRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger, Constants.ObjectTypes.DocumentTypeContainer)
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
internal class DomainRepository : NPocoRepositoryBase<int, IDomain>, IDomainRepository
|
||||
{
|
||||
public DomainRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public DomainRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
private readonly Guid _containerObjectType;
|
||||
|
||||
public EntityContainerRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, Guid containerObjectType)
|
||||
public EntityContainerRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, Guid containerObjectType)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
var allowedContainers = new[] { Constants.ObjectTypes.DocumentTypeContainer, Constants.ObjectTypes.MediaTypeContainer, Constants.ObjectTypes.DataTypeContainer };
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
internal class ExternalLoginRepository : NPocoRepositoryBase<int, IIdentityUserLogin>, IExternalLoginRepository
|
||||
{
|
||||
public ExternalLoginRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public ExternalLoginRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly Dictionary<string, int> _codeIdMap = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Dictionary<int, string> _idCodeMap = new Dictionary<int, string>();
|
||||
|
||||
public LanguageRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public LanguageRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
internal class MacroRepository : NPocoRepositoryBase<int, IMacro>, IMacroRepository
|
||||
{
|
||||
public MacroRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public MacroRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly ITagRepository _tagRepository;
|
||||
private readonly MediaByGuidReadRepository _mediaByGuidReadRepository;
|
||||
|
||||
public MediaRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, ILanguageRepository languageRepository)
|
||||
public MediaRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, cache, languageRepository, logger)
|
||||
{
|
||||
_mediaTypeRepository = mediaTypeRepository ?? throw new ArgumentNullException(nameof(mediaTypeRepository));
|
||||
@@ -395,7 +395,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
private readonly MediaRepository _outerRepo;
|
||||
|
||||
public MediaByGuidReadRepository(MediaRepository outerRepo, IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public MediaByGuidReadRepository(MediaRepository outerRepo, IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
_outerRepo = outerRepo;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
class MediaTypeContainerRepository : EntityContainerRepository, IMediaTypeContainerRepository
|
||||
{
|
||||
public MediaTypeContainerRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public MediaTypeContainerRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger, Constants.ObjectTypes.MediaTypeContainer)
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
internal class MediaTypeRepository : ContentTypeRepositoryBase<IMediaType>, IMediaTypeRepository
|
||||
{
|
||||
public MediaTypeRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public MediaTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
internal class MemberGroupRepository : NPocoRepositoryBase<int, IMemberGroup>, IMemberGroupRepository
|
||||
{
|
||||
public MemberGroupRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public MemberGroupRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
var result = Get(qry);
|
||||
return result.FirstOrDefault();
|
||||
},
|
||||
//cache for 5 mins since that is the default in the RuntimeCacheProvider
|
||||
//cache for 5 mins since that is the default in the Runtime app cache
|
||||
TimeSpan.FromMinutes(5),
|
||||
//sliding is true
|
||||
true);
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly ITagRepository _tagRepository;
|
||||
private readonly IMemberGroupRepository _memberGroupRepository;
|
||||
|
||||
public MemberRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository)
|
||||
public MemberRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository)
|
||||
: base(scopeAccessor, cache, languageRepository, logger)
|
||||
{
|
||||
_memberTypeRepository = memberTypeRepository ?? throw new ArgumentNullException(nameof(memberTypeRepository));
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
internal class MemberTypeRepository : ContentTypeRepositoryBase<IMemberType>, IMemberTypeRepository
|
||||
{
|
||||
public MemberTypeRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public MemberTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NPocoRepositoryBase{TId, TEntity}"/> class.
|
||||
/// </summary>
|
||||
protected NPocoRepositoryBase(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
protected NPocoRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
internal class PermissionRepository<TEntity> : NPocoRepositoryBase<int, ContentPermissionSet>
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
public PermissionRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public PermissionRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
internal class PublicAccessRepository : NPocoRepositoryBase<Guid, PublicAccessEntry>, IPublicAccessRepository
|
||||
{
|
||||
public PublicAccessRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public PublicAccessRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
internal class RedirectUrlRepository : NPocoRepositoryBase<Guid, IRedirectUrl>, IRedirectUrlRepository
|
||||
{
|
||||
public RedirectUrlRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public RedirectUrlRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private readonly IRelationTypeRepository _relationTypeRepository;
|
||||
|
||||
public RelationRepository(IScopeAccessor scopeAccessor, ILogger logger, IRelationTypeRepository relationTypeRepository)
|
||||
: base(scopeAccessor, CacheHelper.NoCache, logger)
|
||||
: base(scopeAccessor, AppCaches.NoCache, logger)
|
||||
{
|
||||
_relationTypeRepository = relationTypeRepository;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// </summary>
|
||||
internal class RelationTypeRepository : NPocoRepositoryBase<int, IRelationType>, IRelationTypeRepository
|
||||
{
|
||||
public RelationTypeRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
public RelationTypeRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{ }
|
||||
|
||||
|
||||
@@ -19,18 +19,18 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
private IRepositoryCachePolicy<TEntity, TId> _cachePolicy;
|
||||
|
||||
protected RepositoryBase(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger)
|
||||
protected RepositoryBase(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger)
|
||||
{
|
||||
ScopeAccessor = scopeAccessor ?? throw new ArgumentNullException(nameof(scopeAccessor));
|
||||
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
GlobalCache = cache ?? throw new ArgumentNullException(nameof(cache));
|
||||
AppCaches = appCaches ?? throw new ArgumentNullException(nameof(appCaches));
|
||||
}
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
|
||||
protected CacheHelper GlobalCache { get; }
|
||||
protected AppCaches AppCaches { get; }
|
||||
|
||||
protected IRuntimeCacheProvider GlobalIsolatedCache => GlobalCache.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
|
||||
protected IAppPolicyCache GlobalIsolatedCache => AppCaches.IsolatedCaches.GetOrCreate<TEntity>();
|
||||
|
||||
protected IScopeAccessor ScopeAccessor { get; }
|
||||
|
||||
@@ -60,18 +60,18 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// Gets the isolated cache.
|
||||
/// </summary>
|
||||
/// <remarks>Depends on the ambient scope cache mode.</remarks>
|
||||
protected IRuntimeCacheProvider IsolatedCache
|
||||
protected IAppPolicyCache IsolatedCache
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (AmbientScope.RepositoryCacheMode)
|
||||
{
|
||||
case RepositoryCacheMode.Default:
|
||||
return GlobalCache.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
|
||||
return AppCaches.IsolatedCaches.GetOrCreate<TEntity>();
|
||||
case RepositoryCacheMode.Scoped:
|
||||
return AmbientScope.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
|
||||
return AmbientScope.IsolatedCaches.GetOrCreate<TEntity>();
|
||||
case RepositoryCacheMode.None:
|
||||
return NullCacheProvider.Instance;
|
||||
return NoAppCache.Instance;
|
||||
default:
|
||||
throw new Exception("oops: cache mode.");
|
||||
}
|
||||
@@ -127,7 +127,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GlobalCache == CacheHelper.NoCache)
|
||||
if (AppCaches == AppCaches.NoCache)
|
||||
return NoCacheRepositoryCachePolicy<TEntity, TId>.Instance;
|
||||
|
||||
// create the cache policy using IsolatedCache which is either global
|
||||
@@ -157,7 +157,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
/// <summary>
|
||||
/// Adds or Updates an entity of type TEntity
|
||||
/// </summary>
|
||||
/// <remarks>This method is backed by an <see cref="IRuntimeCacheProvider"/> cache</remarks>
|
||||
/// <remarks>This method is backed by an <see cref="IAppPolicyCache"/> cache</remarks>
|
||||
/// <param name="entity"></param>
|
||||
public void Save(TEntity entity)
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
internal class ServerRegistrationRepository : NPocoRepositoryBase<int, IServerRegistration>, IServerRegistrationRepository
|
||||
{
|
||||
public ServerRegistrationRepository(IScopeAccessor scopeAccessor, ILogger logger)
|
||||
: base(scopeAccessor, CacheHelper.NoCache, logger)
|
||||
: base(scopeAccessor, AppCaches.NoCache, logger)
|
||||
{ }
|
||||
|
||||
protected override IRepositoryCachePolicy<IServerRegistration, int> CreateCachePolicy()
|
||||
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
// and this is because the repository is special and should not participate in scopes
|
||||
// (cleanup in v8)
|
||||
//
|
||||
return new FullDataSetRepositoryCachePolicy<IServerRegistration, int>(GlobalCache.RuntimeCache, ScopeAccessor, GetEntityId, /*expires:*/ false);
|
||||
return new FullDataSetRepositoryCachePolicy<IServerRegistration, int>(AppCaches.RuntimeCache, ScopeAccessor, GetEntityId, /*expires:*/ false);
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user