diff --git a/src/Umbraco.PublishedCache.NuCache/Property.cs b/src/Umbraco.PublishedCache.NuCache/Property.cs index aa30fc9570cc..e9b5ff088320 100644 --- a/src/Umbraco.PublishedCache.NuCache/Property.cs +++ b/src/Umbraco.PublishedCache.NuCache/Property.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System.Xml.Serialization; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Collections; @@ -19,7 +20,6 @@ internal class Property : PublishedPropertyBase private readonly bool _isMember; private readonly bool _isPreviewing; - private readonly object _locko = new(); private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; // the invariant-neutral source and inter values @@ -33,7 +33,7 @@ internal class Property : PublishedPropertyBase private object? _interValue; // the variant source and inter values - private Dictionary? _sourceValues; + private readonly Lazy> _sourceValues = new(() => new(-1, 5)); private string? _valuesCacheKey; @@ -56,7 +56,7 @@ public Property( PropertyCacheLevel referenceCacheLevel = PropertyCacheLevel.Element) : base(propertyType, referenceCacheLevel) { - if (sourceValues != null) + if (sourceValues is not null) { foreach (PropertyData sourceValue in sourceValues) { @@ -66,12 +66,7 @@ public Property( } else { - if (_sourceValues == null) - { - _sourceValues = new Dictionary(); - } - - _sourceValues[new CompositeStringStringKey(sourceValue.Culture, sourceValue.Segment)] + _sourceValues.Value[new CompositeStringStringKey(sourceValue.Culture, sourceValue.Segment)] = new SourceInterValue { Culture = sourceValue.Culture, @@ -125,30 +120,27 @@ public override bool HasValue(string? culture = null, string? segment = null) return hasValue.Value; } - lock (_locko) + value = GetInterValue(culture, segment); + hasValue = PropertyType.IsValue(value, PropertyValueLevel.Inter); + if (hasValue.HasValue) { - value = GetInterValue(culture, segment); - hasValue = PropertyType.IsValue(value, PropertyValueLevel.Inter); - if (hasValue.HasValue) - { - return hasValue.Value; - } - - CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); + return hasValue.Value; + } - // initial reference cache level always is .Content - const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element; + CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); - if (!cacheValues.ObjectInitialized) - { - cacheValues.ObjectValue = - PropertyType.ConvertInterToObject(_content, initialCacheLevel, value, _isPreviewing); - cacheValues.ObjectInitialized = true; - } + // initial reference cache level always is .Content + const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element; - value = cacheValues.ObjectValue; - return PropertyType.IsValue(value, PropertyValueLevel.Object) ?? false; + if (!cacheValues.ObjectInitialized) + { + cacheValues.ObjectValue = + PropertyType.ConvertInterToObject(_content, initialCacheLevel, value, _isPreviewing); + cacheValues.ObjectInitialized = true; } + + value = cacheValues.ObjectValue; + return PropertyType.IsValue(value, PropertyValueLevel.Object) ?? false; } public override object? GetSourceValue(string? culture = null, string? segment = null) @@ -160,19 +152,11 @@ public override bool HasValue(string? culture = null, string? segment = null) return _sourceValue; } - lock (_locko) - { - if (_sourceValues == null) - { - return null; - } - - return _sourceValues.TryGetValue( - new CompositeStringStringKey(culture, segment), - out SourceInterValue? sourceValue) - ? sourceValue.SourceValue - : null; - } + return _sourceValues.Value.TryGetValue( + new CompositeStringStringKey(culture, segment), + out SourceInterValue? sourceValue) + ? sourceValue.SourceValue + : null; } private CacheValues GetCacheValues(PropertyCacheLevel cacheLevel) @@ -227,7 +211,6 @@ private CacheValues GetCacheValues(IAppCache? cache) return (CacheValues)cache.Get(ValuesCacheKey, () => new CacheValues())!; } - // this is always invoked from within a lock, so does not require its own lock private object? GetInterValue(string? culture, string? segment) { if (culture == string.Empty && segment == string.Empty) @@ -242,21 +225,14 @@ private CacheValues GetCacheValues(IAppCache? cache) return _interValue; } - if (_sourceValues == null) - { - _sourceValues = new Dictionary(); - } - var k = new CompositeStringStringKey(culture, segment); - if (!_sourceValues.TryGetValue(k, out SourceInterValue? vvalue)) + + SourceInterValue vvalue = _sourceValues.Value.GetOrAdd(k, _ => new SourceInterValue { - _sourceValues[k] = vvalue = new SourceInterValue - { - Culture = culture, - Segment = segment, - SourceValue = GetSourceValue(culture, segment), - }; - } + Culture = culture, + Segment = segment, + SourceValue = GetSourceValue(culture, segment), + }); if (vvalue.InterInitialized) { @@ -273,23 +249,20 @@ private CacheValues GetCacheValues(IAppCache? cache) _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment); object? value; - lock (_locko) - { - CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); + CacheValue cacheValues = GetCacheValues(PropertyType.CacheLevel).For(culture, segment); - // initial reference cache level always is .Content - const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element; + // initial reference cache level always is .Content + const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element; - if (cacheValues.ObjectInitialized) - { - return cacheValues.ObjectValue; - } - - cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing); - cacheValues.ObjectInitialized = true; - value = cacheValues.ObjectValue; + if (cacheValues.ObjectInitialized) + { + return cacheValues.ObjectValue; } + cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing); + cacheValues.ObjectInitialized = true; + value = cacheValues.ObjectValue; + return value; } @@ -298,18 +271,15 @@ private CacheValues GetCacheValues(IAppCache? cache) _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment); object? value; - lock (_locko) - { - CacheValue cacheValues = GetCacheValues(expanding ? PropertyType.DeliveryApiCacheLevelForExpansion : PropertyType.DeliveryApiCacheLevel).For(culture, segment); + CacheValue cacheValues = GetCacheValues(expanding ? PropertyType.DeliveryApiCacheLevelForExpansion : PropertyType.DeliveryApiCacheLevel).For(culture, segment); - // initial reference cache level always is .Content - const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element; + // initial reference cache level always is .Content + const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element; - object? GetDeliveryApiObject() => PropertyType.ConvertInterToDeliveryApiObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing, expanding); - value = expanding - ? GetDeliveryApiExpandedObject(cacheValues, GetDeliveryApiObject) - : GetDeliveryApiDefaultObject(cacheValues, GetDeliveryApiObject); - } + object? GetDeliveryApiObject() => PropertyType.ConvertInterToDeliveryApiObject(_content, initialCacheLevel, GetInterValue(culture, segment), _isPreviewing, expanding); + value = expanding + ? GetDeliveryApiExpandedObject(cacheValues, GetDeliveryApiObject) + : GetDeliveryApiDefaultObject(cacheValues, GetDeliveryApiObject); return value; } @@ -359,9 +329,8 @@ private class CacheValue private class CacheValues : CacheValue { - private Dictionary? _values; + private readonly Lazy> _values = new(() => new(-1, 5)); - // this is always invoked from within a lock, so does not require its own lock public CacheValue For(string? culture, string? segment) { if (culture == string.Empty && segment == string.Empty) @@ -369,16 +338,9 @@ public CacheValue For(string? culture, string? segment) return this; } - if (_values == null) - { - _values = new Dictionary(); - } - var k = new CompositeStringStringKey(culture, segment); - if (!_values.TryGetValue(k, out CacheValue? value)) - { - _values[k] = value = new CacheValue(); - } + + CacheValue value = _values.Value.GetOrAdd(k, _ => new CacheValue()); return value; }