diff --git a/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs b/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs index 0ae721943d..14fef80f0d 100644 --- a/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs +++ b/src/Umbraco.Core/Cache/DeepCloneRuntimeCacheProvider.cs @@ -26,6 +26,9 @@ namespace Umbraco.Core.Cache public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider) { + if (innerProvider.GetType() == typeof(DeepCloneRuntimeCacheProvider)) + throw new InvalidOperationException("A " + typeof(DeepCloneRuntimeCacheProvider) + " cannot wrap another instance of " + typeof(DeepCloneRuntimeCacheProvider)); + InnerProvider = innerProvider; } @@ -105,9 +108,11 @@ namespace Umbraco.Core.Cache 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 out of the cache return CheckCloneableAndTracksChanges(cached); } diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index a4d71885f8..b8b4ac7818 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -34,7 +34,7 @@ namespace Umbraco.Core.Persistence.Repositories : base(work, cache, logger, sqlSyntax) { _contentTypeRepository = contentTypeRepository; - _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); + _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); } #region Overrides of RepositoryBase @@ -276,47 +276,33 @@ AND umbracoNode.id <> @id", public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId) { - var cached = RuntimeCache.GetCacheItemsByKeySearch(GetPrefixedCacheKey(dataTypeId)); - if (cached != null && cached.Any()) - { - //return from the cache, ensure it's a cloned result - return (PreValueCollection)cached.First().DeepClone(); - } - - return GetAndCachePreValueCollection(dataTypeId); - } - - internal static string GetCacheKeyRegex(int preValueId) - { - return CacheKeys.DataTypePreValuesCacheKey + @"[-\d]+-([\d]*,)*" + preValueId + @"(?!\d)[,\d$]*"; + var collection = GetCachedPreValueCollection(dataTypeId); + return collection; } + /// + /// Gets a specific PreValue by its Id + /// + /// Id of the PreValue to retrieve the value from + /// PreValue as a string public string GetPreValueAsString(int preValueId) { - //We need to see if we can find the cached PreValueCollection based on the cache key above + var collections = RuntimeCache.GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_"); - var cached = RuntimeCache.GetCacheItemsByKeyExpression(GetCacheKeyRegex(preValueId)); - if (cached != null && cached.Any()) - { - //return from the cache - var collection = cached.First(); - var preVal = collection.FormatAsDictionary().Single(x => x.Value.Id == preValueId); - return preVal.Value.Value; - } + var preValue = collections.SelectMany(x => x.FormatAsDictionary().Values).FirstOrDefault(x => x.Id == preValueId); + if (preValue != null) + return preValue.Value; - //go and find the data type id for the pre val id passed in - - var dto = Database.FirstOrDefault("WHERE id = @preValueId", new { preValueId = preValueId }); + var dto = Database.FirstOrDefault("WHERE id = @preValueId", new { preValueId }); if (dto == null) - { return string.Empty; - } - // go cache the collection - var preVals = GetAndCachePreValueCollection(dto.DataTypeNodeId); - //return the single value for this id - var pv = preVals.FormatAsDictionary().Single(x => x.Value.Id == preValueId); - return pv.Value.Value; + var collection = GetCachedPreValueCollection(dto.DataTypeNodeId); + if (collection == null) + return string.Empty; + + preValue = collection.FormatAsDictionary().Values.FirstOrDefault(x => x.Id == preValueId); + return preValue == null ? string.Empty : preValue.Value; } public void AddOrUpdatePreValues(int dataTypeId, IDictionary values) @@ -441,40 +427,28 @@ AND umbracoNode.id <> @id", sortOrder++; } - } - private string GetPrefixedCacheKey(int dataTypeId) + private static string GetPrefixedCacheKey(int dataTypeId) { - return CacheKeys.DataTypePreValuesCacheKey + dataTypeId + "-"; + return CacheKeys.DataTypePreValuesCacheKey + "_" + dataTypeId; } - private PreValueCollection GetAndCachePreValueCollection(int dataTypeId) + private PreValueCollection GetCachedPreValueCollection(int datetypeId) { - //go get the data - var dtos = Database.Fetch("WHERE datatypeNodeId = @Id", new { Id = dataTypeId }); - var list = dtos.Select(x => new Tuple(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList(); - var collection = PreValueConverter.ConvertToPreValuesCollection(list); - - //now create the cache key, this needs to include all pre-value ids so that we can use this cached item in the GetPreValuesAsString method - //the key will be: "UmbracoPreValDATATYPEID-CSVOFPREVALIDS - - var key = GetPrefixedCacheKey(dataTypeId) - + string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray()); - - //store into cache - RuntimeCache.InsertCacheItem(key, () => collection, - //30 mins - new TimeSpan(0, 0, 30), - //sliding is true - true); - - return collection; + var key = GetPrefixedCacheKey(datetypeId); + return RuntimeCache.GetCacheItem(key, () => + { + var dtos = Database.Fetch("WHERE datatypeNodeId = @Id", new { Id = datetypeId }); + var list = dtos.Select(x => new Tuple(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList(); + var collection = PreValueConverter.ConvertToPreValuesCollection(list); + return collection; + }, TimeSpan.FromMinutes(20), isSliding: true); } private string EnsureUniqueNodeName(string nodeName, int id = 0) { - + var sql = new Sql(); sql.Select("*") @@ -576,7 +550,7 @@ AND umbracoNode.id <> @id", } //NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT - // in reality there is no need to check the uniqueness of this alias because the only way that this code executes is + // in reality there is no need to check the uniqueness of this alias because the only way that this code executes is // based on an IDictionary dictionary being passed to this repository and a dictionary // must have unique aliases by definition, so there is no need for this additional check @@ -596,10 +570,10 @@ AND umbracoNode.id <> @id", { throw new InvalidOperationException("Cannot update a pre value for a data type that has no identity"); } - + //NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT // this causes issues when sorting the pre-values (http://issues.umbraco.org/issue/U4-5670) but in reality - // there is no need to check the uniqueness of this alias because the only way that this code executes is + // there is no need to check the uniqueness of this alias because the only way that this code executes is // based on an IDictionary dictionary being passed to this repository and a dictionary // must have unique aliases by definition, so there is no need for this additional check @@ -614,7 +588,7 @@ AND umbracoNode.id <> @id", Database.Update(dto); } - + } internal static class PreValueConverter diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index ff0f9e9028..bfb92ccf1a 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -48,7 +48,12 @@ namespace Umbraco.Core.Persistence _cacheHelper.IsolatedRuntimeCache.CacheFactory = type => { var cache = origFactory(type); - return new DeepCloneRuntimeCacheProvider(cache); + + //if the result is already a DeepCloneRuntimeCacheProvider then return it, otherwise + //wrap the result with a DeepCloneRuntimeCacheProvider + return cache is DeepCloneRuntimeCacheProvider + ? cache + : new DeepCloneRuntimeCacheProvider(cache); }; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index 4db63f6085..9f7231037e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -46,23 +46,6 @@ namespace Umbraco.Tests.Persistence.Repositories return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, Constants.ObjectTypes.DataTypeContainerGuid); } - [TestCase("UmbracoPreVal87-21,3,48", 3, true)] - [TestCase("UmbracoPreVal87-21,33,48", 3, false)] - [TestCase("UmbracoPreVal87-21,33,48", 33, true)] - [TestCase("UmbracoPreVal87-21,3,48", 33, false)] - [TestCase("UmbracoPreVal87-21,3,48", 21, true)] - [TestCase("UmbracoPreVal87-21,3,48", 48, true)] - [TestCase("UmbracoPreVal87-22,33,48", 2, false)] - [TestCase("UmbracoPreVal87-22,33,48", 22, true)] - [TestCase("UmbracoPreVal87-22,33,44", 4, false)] - [TestCase("UmbracoPreVal87-22,33,44", 44, true)] - [TestCase("UmbracoPreVal87-22,333,44", 33, false)] - [TestCase("UmbracoPreVal87-22,333,44", 333, true)] - public void Pre_Value_Cache_Key_Tests(string cacheKey, int preValueId, bool outcome) - { - Assert.AreEqual(outcome, Regex.IsMatch(cacheKey, DataTypeDefinitionRepository.GetCacheKeyRegex(preValueId))); - } - [Test] public void Can_Move() { @@ -537,7 +520,7 @@ namespace Umbraco.Tests.Persistence.Repositories } var cached = cache.IsolatedRuntimeCache.GetCache().Result - .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-"); + .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_" + dtd.Id); Assert.IsNotNull(cached); Assert.AreEqual(1, cached.Count()); @@ -581,7 +564,7 @@ namespace Umbraco.Tests.Persistence.Repositories } var cached = cache.IsolatedRuntimeCache.GetCache().Result - .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-"); + .GetCacheItemsByKeySearch(CacheKeys.DataTypePreValuesCacheKey + "_" + dtd.Id); Assert.IsNotNull(cached); Assert.AreEqual(1, cached.Count()); diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index 11b3ab6294..2300082ece 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -101,13 +101,13 @@ namespace Umbraco.Web.Cache ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.IdToKeyCacheKey); ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.KeyToIdCacheKey); + var dataTypeCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); payloads.ForEach(payload => { //clears the prevalue cache - var dataTypeCache = ApplicationContext.Current.ApplicationCache.IsolatedRuntimeCache.GetCache(); if (dataTypeCache) - dataTypeCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.DataTypePreValuesCacheKey, payload.Id)); - + dataTypeCache.Result.ClearCacheByKeySearch(string.Format("{0}_{1}", CacheKeys.DataTypePreValuesCacheKey, payload.Id)); + PublishedContentType.ClearDataType(payload.Id); }); diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 3c97551eaf..5befbda7cf 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -1389,10 +1389,15 @@ namespace Umbraco.Web return test ? new HtmlString(valueIfTrue) : new HtmlString(string.Empty); } - #endregion + #endregion #region Prevalues + /// + /// Gets a specific PreValue by its Id + /// + /// Id of the PreValue to retrieve the value from + /// PreValue as a string public string GetPreValueAsString(int id) { return DataTypeService.GetPreValueAsString(id);