From 3ecb9d0f862347d515dd5e70890e14d77c34b952 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 22 Jan 2016 17:05:27 +0100 Subject: [PATCH] U4-7807 Domain, language, public access cache (GetAll) caches not working when there are no items --- .../Cache/DefaultRepositoryCachePolicy.cs | 13 +++++- .../Cache/FullDataSetRepositoryCachePolicy.cs | 29 +++++++++++- ...FullDataSetRepositoryCachePolicyFactory.cs | 2 +- .../Cache/FullDataSetCachePolicyTests.cs | 45 ++++++++++++++++++- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs index 58b3a3956e..66d9b2ac25 100644 --- a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs @@ -158,8 +158,7 @@ namespace Umbraco.Core.Cache else if (_options.GetAllCacheAllowZeroCount) { //if the repository allows caching a zero count, then check the zero count cache - var zeroCount = Cache.GetCacheItem(GetCacheTypeKey()); - if (zeroCount != null && zeroCount.Any() == false) + if (HasZeroCountCache()) { //there is a zero count cache so return an empty list return new TEntity[] {}; @@ -179,6 +178,16 @@ namespace Umbraco.Core.Cache return entityCollection; } + /// + /// Looks up the zero count cache, must return null if it doesn't exist + /// + /// + protected virtual bool HasZeroCountCache() + { + var zeroCount = Cache.GetCacheItem(GetCacheTypeKey()); + return (zeroCount != null && zeroCount.Any() == false); + } + /// /// Performs the lookup for all entities of this type from the cache /// diff --git a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs index c4c86b2ec7..40b100ef67 100644 --- a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Linq; using Umbraco.Core.Collections; using Umbraco.Core.Models.EntityBase; @@ -12,10 +14,18 @@ namespace Umbraco.Core.Cache internal class FullDataSetRepositoryCachePolicy : DefaultRepositoryCachePolicy where TEntity : class, IAggregateRoot { - public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache) : base(cache, new RepositoryCachePolicyOptions()) + public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache) : base(cache, + new RepositoryCachePolicyOptions + { + //Definitely allow zero'd cache entires since this is a full set, in many cases there will be none, + // and we must cache this! + GetAllCacheAllowZeroCount = true + }) { } + private bool? _hasZeroCountCache; + /// /// For this type of caching policy, we don't cache individual items /// @@ -44,6 +54,19 @@ namespace Umbraco.Core.Cache }); } + /// + /// Looks up the zero count cache, must return null if it doesn't exist + /// + /// + protected override bool HasZeroCountCache() + { + if (_hasZeroCountCache.HasValue) + return _hasZeroCountCache.Value; + + _hasZeroCountCache = Cache.GetCacheItem>(GetCacheTypeKey()) != null; + return _hasZeroCountCache.Value; + } + /// /// This policy will cache the full data set as a single collection /// @@ -51,6 +74,10 @@ namespace Umbraco.Core.Cache protected override TEntity[] GetAllFromCache() { var found = Cache.GetCacheItem>(GetCacheTypeKey()); + + //This method will get called before checking for zero count cache, so we'll just set the flag here + _hasZeroCountCache = found != null; + return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray(); } } diff --git a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs index 470db33b6a..75bdae7e83 100644 --- a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs +++ b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Cache public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache) { - _runtimeCache = runtimeCache; + _runtimeCache = runtimeCache; } public virtual IRepositoryCachePolicy CreatePolicy() diff --git a/src/Umbraco.Tests/Cache/FullDataSetCachePolicyTests.cs b/src/Umbraco.Tests/Cache/FullDataSetCachePolicyTests.cs index 7fd894e2a9..3d3884c686 100644 --- a/src/Umbraco.Tests/Cache/FullDataSetCachePolicyTests.cs +++ b/src/Umbraco.Tests/Cache/FullDataSetCachePolicyTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Web.Caching; using Moq; using NUnit.Framework; @@ -13,6 +14,48 @@ namespace Umbraco.Tests.Cache [TestFixture] public class FullDataSetCachePolicyTests { + [Test] + public void Get_All_Caches_Empty_List() + { + var cached = new List(); + + IList list = null; + + var cache = new Mock(); + cache.Setup(x => x.InsertCacheItem(It.IsAny(), It.IsAny>(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Callback((string cacheKey, Func o, TimeSpan? t, bool b, CacheItemPriority cip, CacheItemRemovedCallback circ, string[] s) => + { + cached.Add(cacheKey); + + list = o() as IList; + }); + cache.Setup(x => x.GetCacheItem(It.IsAny())).Returns(() => + { + //return null if this is the first pass + return cached.Any() ? new DeepCloneableList() : null; + }); + + var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object); + using (defaultPolicy) + { + var found = defaultPolicy.GetAll(new object[] {}, o => new AuditItem[] {}); + } + + Assert.AreEqual(1, cached.Count); + Assert.IsNotNull(list); + + //Do it again, ensure that its coming from the cache! + defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object); + using (defaultPolicy) + { + var found = defaultPolicy.GetAll(new object[] { }, o => new AuditItem[] { }); + } + + Assert.AreEqual(1, cached.Count); + Assert.IsNotNull(list); + } + [Test] public void Get_All_Caches_As_Single_List() { @@ -28,7 +71,7 @@ namespace Umbraco.Tests.Cache list = o() as IList; }); - cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny())).Returns(new AuditItem[] { }); + cache.Setup(x => x.GetCacheItem(It.IsAny())).Returns(new AuditItem[] { }); var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object); using (defaultPolicy)