diff --git a/src/Umbraco.Core/Collections/CompositeIntStringKey.cs b/src/Umbraco.Core/Collections/CompositeIntStringKey.cs
new file mode 100644
index 0000000000..eb9db80990
--- /dev/null
+++ b/src/Umbraco.Core/Collections/CompositeIntStringKey.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace Umbraco.Core.Collections
+{
+ ///
+ /// Represents a composite key of (int, string) for fast dictionaries.
+ ///
+ ///
+ /// The integer part of the key must be greater than, or equal to, zero.
+ /// The string part of the key is case-insensitive.
+ /// Null is a valid value for both parts.
+ ///
+ public struct CompositeIntStringKey : IEquatable
+ {
+ private readonly int _key1;
+ private readonly string _key2;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ public CompositeIntStringKey(int? key1, string key2)
+ {
+ if (key1 < 0) throw new ArgumentOutOfRangeException(nameof(key1));
+ _key1 = key1 ?? -1;
+ _key2 = key2?.ToLowerInvariant() ?? "NULL";
+ }
+
+ public bool Equals(CompositeIntStringKey other)
+ => _key2 == other._key2 && _key1 == other._key1;
+
+ public override bool Equals(object obj)
+ => obj is CompositeIntStringKey other && _key2 == other._key2 && _key1 == other._key1;
+
+ public override int GetHashCode()
+ => _key2.GetHashCode() * 31 + _key1;
+
+ public static bool operator ==(CompositeIntStringKey key1, CompositeIntStringKey key2)
+ => key1._key2 == key2._key2 && key1._key1 == key2._key1;
+
+ public static bool operator !=(CompositeIntStringKey key1, CompositeIntStringKey key2)
+ => key1._key2 != key2._key2 || key1._key1 != key2._key1;
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index 6805eb3504..0dad772a91 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -339,6 +339,9 @@ namespace Umbraco.Core.Models
var published = CopyingFromSelf(other);
+ // segment is invariant in comparisons
+ segment = segment?.ToLowerInvariant();
+
// note: use property.SetValue(), don't assign pvalue.EditValue, else change tracking fails
// clear all existing properties
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index d90084f60b..0d8ab300c8 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
+using Umbraco.Core.Collections;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
@@ -15,12 +16,11 @@ namespace Umbraco.Core.Models
[DataContract(IsReference = true)]
public class Property : Entity
{
- private PropertyType _propertyType;
private List _tagChanges;
private List _values = new List();
private PropertyValue _pvalue;
- private Dictionary _vvalues;
+ private Dictionary _vvalues;
private static readonly Lazy Ps = new Lazy();
@@ -29,51 +29,30 @@ namespace Umbraco.Core.Models
public Property(PropertyType propertyType)
{
- _propertyType = propertyType;
+ PropertyType = propertyType;
}
public Property(int id, PropertyType propertyType)
{
Id = id;
- _propertyType = propertyType;
+ PropertyType = propertyType;
}
public class PropertyValue
{
+ private string _segment;
+
public int? LanguageId { get; internal set; }
- public string Segment { get; internal set; }
+ public string Segment
+ {
+ get => _segment;
+ internal set => _segment = value?.ToLowerInvariant();
+ }
public object EditedValue { get; internal set; }
public object PublishedValue { get; internal set; }
public PropertyValue Clone()
- => new PropertyValue { LanguageId = LanguageId, Segment = Segment, PublishedValue = PublishedValue, EditedValue = EditedValue };
- }
-
- private struct CompositeKey : IEquatable
- {
- private readonly int _key1;
- private readonly string _key2;
-
- public CompositeKey(int? key1, string key2)
- {
- _key1 = key1 ?? -1;
- _key2 = key2?.ToLowerInvariant() ?? "NEUTRAL";
- }
-
- public bool Equals(CompositeKey other)
- => _key2 == other._key2 && _key1 == other._key1;
-
- public override bool Equals(object obj)
- => obj is CompositeKey other && _key2 == other._key2 && _key1 == other._key1;
-
- public override int GetHashCode()
- => _key2.GetHashCode() * 31 + _key1;
-
- public static bool operator ==(CompositeKey key1, CompositeKey key2)
- => key1._key2 == key2._key2 && key1._key1 == key2._key1;
-
- public static bool operator !=(CompositeKey key1, CompositeKey key2)
- => key1._key2 != key2._key2 || key1._key1 != key2._key1;
+ => new PropertyValue { LanguageId = LanguageId, _segment = _segment, PublishedValue = PublishedValue, EditedValue = EditedValue };
}
// ReSharper disable once ClassNeverInstantiated.Local
@@ -106,7 +85,7 @@ namespace Umbraco.Core.Models
/// Returns the PropertyType, which this Property is based on
///
[IgnoreDataMember]
- public PropertyType PropertyType => _propertyType;
+ public PropertyType PropertyType { get; private set; }
///
/// Gets the list of values.
@@ -119,10 +98,10 @@ namespace Umbraco.Core.Models
{
// make sure we filter out invalid variations
// make sure we leave _vvalues null if possible
- _values = value.Where(x => _propertyType.ValidateVariation(x.LanguageId, x.Segment, false)).ToList();
+ _values = value.Where(x => PropertyType.ValidateVariation(x.LanguageId, x.Segment, false)).ToList();
_pvalue = _values.FirstOrDefault(x => !x.LanguageId.HasValue && x.Segment == null);
_vvalues = _values.Count > (_pvalue == null ? 0 : 1)
- ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeKey(x.LanguageId, x.Segment), x => x)
+ ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeIntStringKey(x.LanguageId, x.Segment), x => x)
: null;
}
}
@@ -141,13 +120,13 @@ namespace Umbraco.Core.Models
/// Returns the Alias of the PropertyType, which this Property is based on
///
[DataMember]
- public string Alias => _propertyType.Alias;
+ public string Alias => PropertyType.Alias;
///
/// Returns the Id of the PropertyType, which this Property is based on
///
[IgnoreDataMember]
- internal int PropertyTypeId => _propertyType.Id;
+ internal int PropertyTypeId => PropertyType.Id;
///
/// Returns the DatabaseType that the underlaying DataType is using to store its values
@@ -156,17 +135,17 @@ namespace Umbraco.Core.Models
/// Only used internally when saving the property value.
///
[IgnoreDataMember]
- internal DataTypeDatabaseType DataTypeDatabaseType => _propertyType.DataTypeDatabaseType;
+ internal DataTypeDatabaseType DataTypeDatabaseType => PropertyType.DataTypeDatabaseType;
///
/// Gets the value.
///
public object GetValue(int? languageId = null, string segment = null, bool published = false)
{
- if (!_propertyType.ValidateVariation(languageId, segment, false)) return null;
+ if (!PropertyType.ValidateVariation(languageId, segment, false)) return null;
if (!languageId.HasValue && segment == null) return GetPropertyValue(_pvalue, published);
if (_vvalues == null) return null;
- return _vvalues.TryGetValue(new CompositeKey(languageId, segment), out var pvalue)
+ return _vvalues.TryGetValue(new CompositeIntStringKey(languageId, segment), out var pvalue)
? GetPropertyValue(pvalue, published)
: null;
}
@@ -175,7 +154,7 @@ namespace Umbraco.Core.Models
{
if (pvalue == null) return null;
- return _propertyType.IsPublishing
+ return PropertyType.IsPublishing
? (published ? pvalue.PublishedValue : pvalue.EditedValue)
: pvalue.EditedValue;
}
@@ -185,14 +164,14 @@ namespace Umbraco.Core.Models
internal void PublishAllValues()
{
// if invariant-neutral is supported, publish invariant-neutral
- if (_propertyType.ValidateVariation(null, null, false))
+ if (PropertyType.ValidateVariation(null, null, false))
PublishPropertyValue(_pvalue);
// publish everything not invariant-neutral that is supported
if (_vvalues != null)
{
var pvalues = _vvalues
- .Where(x => _propertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
.Select(x => x.Value);
foreach (var pvalue in pvalues)
PublishPropertyValue(pvalue);
@@ -203,7 +182,7 @@ namespace Umbraco.Core.Models
// does *not* validate the value - content item must validate first
internal void PublishValue(int? languageId = null, string segment = null)
{
- _propertyType.ValidateVariation(languageId, segment, true);
+ PropertyType.ValidateVariation(languageId, segment, true);
(var pvalue, _) = GetPValue(languageId, segment, false);
if (pvalue == null) return;
@@ -215,7 +194,7 @@ namespace Umbraco.Core.Models
internal void PublishCultureValues(int? languageId = null)
{
// if invariant and invariant-neutral is supported, publish invariant-neutral
- if (!languageId.HasValue && _propertyType.ValidateVariation(null, null, false))
+ if (!languageId.HasValue && PropertyType.ValidateVariation(null, null, false))
PublishPropertyValue(_pvalue);
// publish everything not invariant-neutral that matches the culture and is supported
@@ -223,7 +202,7 @@ namespace Umbraco.Core.Models
{
var pvalues = _vvalues
.Where(x => x.Value.LanguageId == languageId)
- .Where(x => _propertyType.ValidateVariation(languageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false))
.Select(x => x.Value);
foreach (var pvalue in pvalues)
PublishPropertyValue(pvalue);
@@ -233,13 +212,13 @@ namespace Umbraco.Core.Models
// internal - must be invoked by the content item
internal void ClearPublishedAllValues()
{
- if (_propertyType.ValidateVariation(null, null, false))
+ if (PropertyType.ValidateVariation(null, null, false))
ClearPublishedPropertyValue(_pvalue);
if (_vvalues != null)
{
var pvalues = _vvalues
- .Where(x => _propertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
.Select(x => x.Value);
foreach (var pvalue in pvalues)
ClearPublishedPropertyValue(pvalue);
@@ -249,7 +228,7 @@ namespace Umbraco.Core.Models
// internal - must be invoked by the content item
internal void ClearPublishedValue(int? languageId = null, string segment = null)
{
- _propertyType.ValidateVariation(languageId, segment, true);
+ PropertyType.ValidateVariation(languageId, segment, true);
(var pvalue, _) = GetPValue(languageId, segment, false);
if (pvalue == null) return;
ClearPublishedPropertyValue(pvalue);
@@ -258,14 +237,14 @@ namespace Umbraco.Core.Models
// internal - must be invoked by the content item
internal void ClearPublishedCultureValues(int? languageId = null)
{
- if (!languageId.HasValue && _propertyType.ValidateVariation(null, null, false))
+ if (!languageId.HasValue && PropertyType.ValidateVariation(null, null, false))
ClearPublishedPropertyValue(_pvalue);
if (_vvalues != null)
{
var pvalues = _vvalues
.Where(x => x.Value.LanguageId == languageId)
- .Where(x => _propertyType.ValidateVariation(languageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false))
.Select(x => x.Value);
foreach (var pvalue in pvalues)
ClearPublishedPropertyValue(pvalue);
@@ -276,7 +255,7 @@ namespace Umbraco.Core.Models
{
if (pvalue == null) return;
- if (!_propertyType.IsPublishing)
+ if (!PropertyType.IsPublishing)
throw new NotSupportedException("Property type does not support publishing.");
var origValue = pvalue.PublishedValue;
pvalue.PublishedValue = ConvertSetValue(pvalue.EditedValue);
@@ -287,7 +266,7 @@ namespace Umbraco.Core.Models
{
if (pvalue == null) return;
- if (!_propertyType.IsPublishing)
+ if (!PropertyType.IsPublishing)
throw new NotSupportedException("Property type does not support publishing.");
var origValue = pvalue.PublishedValue;
pvalue.PublishedValue = ConvertSetValue(null);
@@ -299,7 +278,7 @@ namespace Umbraco.Core.Models
///
public void SetValue(object value, int? languageId = null, string segment = null)
{
- _propertyType.ValidateVariation(languageId, segment, true);
+ PropertyType.ValidateVariation(languageId, segment, true);
(var pvalue, var change) = GetPValue(languageId, segment, true);
var origValue = pvalue.EditedValue;
@@ -315,7 +294,7 @@ namespace Umbraco.Core.Models
{
(var pvalue, _) = GetPValue(languageId, segment, true);
- if (published && _propertyType.IsPublishing)
+ if (published && PropertyType.IsPublishing)
pvalue.PublishedValue = value;
else
pvalue.EditedValue = value;
@@ -343,10 +322,10 @@ namespace Umbraco.Core.Models
if (_vvalues == null)
{
if (!create) return (null, false);
- _vvalues = new Dictionary();
+ _vvalues = new Dictionary();
change = true;
}
- var k = new CompositeKey(languageId, segment);
+ var k = new CompositeIntStringKey(languageId, segment);
if (!_vvalues.TryGetValue(k, out var pvalue))
{
if (!create) return (null, false);
@@ -361,7 +340,7 @@ namespace Umbraco.Core.Models
private object ConvertSetValue(object value)
{
- var isOfExpectedType = _propertyType.IsPropertyTypeValid(value);
+ var isOfExpectedType = PropertyType.IsPropertyTypeValid(value);
if (isOfExpectedType)
return value;
@@ -372,7 +351,7 @@ namespace Umbraco.Core.Models
var s = value.ToString();
- switch (_propertyType.DataTypeDatabaseType)
+ switch (PropertyType.DataTypeDatabaseType)
{
case DataTypeDatabaseType.Nvarchar:
case DataTypeDatabaseType.Ntext:
@@ -382,14 +361,14 @@ namespace Umbraco.Core.Models
if (s.IsNullOrWhiteSpace())
return null; // assume empty means null
var convInt = value.TryConvertTo();
- if (convInt == false) ThrowTypeException(value, typeof(int), _propertyType.Alias);
+ if (convInt == false) ThrowTypeException(value, typeof(int), PropertyType.Alias);
return convInt.Result;
case DataTypeDatabaseType.Decimal:
if (s.IsNullOrWhiteSpace())
return null; // assume empty means null
var convDecimal = value.TryConvertTo();
- if (convDecimal == false) ThrowTypeException(value, typeof(decimal), _propertyType.Alias);
+ if (convDecimal == false) ThrowTypeException(value, typeof(decimal), PropertyType.Alias);
// need to normalize the value (change the scaling factor and remove trailing zeroes)
// because the underlying database is going to mess with the scaling factor anyways.
return convDecimal.Result.Normalize();
@@ -398,7 +377,7 @@ namespace Umbraco.Core.Models
if (s.IsNullOrWhiteSpace())
return null; // assume empty means null
var convDateTime = value.TryConvertTo();
- if (convDateTime == false) ThrowTypeException(value, typeof(DateTime), _propertyType.Alias);
+ if (convDateTime == false) ThrowTypeException(value, typeof(DateTime), PropertyType.Alias);
return convDateTime.Result;
}
@@ -418,7 +397,7 @@ namespace Umbraco.Core.Models
{
// invariant-neutral is supported, validate invariant-neutral
// includes mandatory validation
- if (_propertyType.ValidateVariation(null, null, false) && !IsValidValue(_pvalue)) return false;
+ if (PropertyType.ValidateVariation(null, null, false) && !IsValidValue(_pvalue)) return false;
// either invariant-neutral is not supported, or it is valid
// for anything else, validate the existing values (including mandatory),
@@ -427,7 +406,7 @@ namespace Umbraco.Core.Models
if (_vvalues == null) return true;
var pvalues = _vvalues
- .Where(x => _propertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(x.Value.LanguageId, x.Value.Segment, false))
.Select(x => x.Value)
.ToArray();
@@ -442,7 +421,7 @@ namespace Umbraco.Core.Models
{
// culture-neutral is supported, validate culture-neutral
// includes mandatory validation
- if (_propertyType.ValidateVariation(languageId, null, false) && !IsValidValue(GetValue(languageId)))
+ if (PropertyType.ValidateVariation(languageId, null, false) && !IsValidValue(GetValue(languageId)))
return false;
// either culture-neutral is not supported, or it is valid
@@ -453,7 +432,7 @@ namespace Umbraco.Core.Models
var pvalues = _vvalues
.Where(x => x.Value.LanguageId == languageId)
- .Where(x => _propertyType.ValidateVariation(languageId, x.Value.Segment, false))
+ .Where(x => PropertyType.ValidateVariation(languageId, x.Value.Segment, false))
.Select(x => x.Value)
.ToArray();
@@ -477,7 +456,7 @@ namespace Umbraco.Core.Models
/// True is property value is valid, otherwise false
private bool IsValidValue(object value)
{
- return _propertyType.IsValidPropertyValue(value);
+ return PropertyType.IsValidPropertyValue(value);
}
public override object DeepClone()
@@ -488,7 +467,7 @@ namespace Umbraco.Core.Models
clone.DisableChangeTracking();
//need to manually assign since this is a readonly property
- clone._propertyType = (PropertyType) PropertyType.DeepClone();
+ clone.PropertyType = (PropertyType) PropertyType.DeepClone();
//re-enable tracking
clone.ResetDirtyProperties(false); // not needed really, since we're not tracking
diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs
index 405687cf68..448a91238b 100644
--- a/src/Umbraco.Core/Services/ContentService.cs
+++ b/src/Umbraco.Core/Services/ContentService.cs
@@ -1148,6 +1148,8 @@ namespace Umbraco.Core.Services
///
public IEnumerable SaveAndPublishBranch(IContent content, bool force, int? languageId = null, string segment = null, int userId = 0)
{
+ segment = segment?.ToLowerInvariant();
+
bool IsEditing(IContent c, int? l, string s)
=> c.Properties.Any(x => x.Values.Where(y => y.LanguageId == l && y.Segment == s).Any(y => y.EditedValue != y.PublishedValue));
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 8e5b4170eb..ed61dd874d 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -139,6 +139,7 @@
+
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
index 62eb5f3222..c74e115df9 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/Property.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;
using Umbraco.Core.Cache;
+using Umbraco.Core.Collections;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors;
using Umbraco.Web.PublishedCache.NuCache.DataSource;
@@ -21,12 +22,15 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly object _locko = new object();
+ // the invariant-neutral source and inter values
private readonly object _sourceValue;
private bool _interInitialized;
private object _interValue;
- private List _sourceVValues;
+ // the variant source and inter values
+ private Dictionary _sourceValues;
+ // the variant and non-variant object values
private CacheValues _cacheValues;
private string _valuesCacheKey;
@@ -51,9 +55,10 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
else
{
- if (_sourceVValues == null)
- _sourceVValues = new List();
- _sourceVValues.Add(new PropertyValue { LanguageId = sourceValue.LanguageId, Segment = sourceValue.Segment, SourceValue = sourceValue.Value });
+ if (_sourceValues == null)
+ _sourceValues = new Dictionary();
+ _sourceValues[new CompositeIntStringKey(sourceValue.LanguageId, sourceValue.Segment)]
+ = new SourceInterValue { LanguageId = sourceValue.LanguageId, Segment = sourceValue.Segment, SourceValue = sourceValue.Value };
}
}
}
@@ -70,7 +75,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
: base(origin.PropertyType, origin.ReferenceCacheLevel)
{
_sourceValue = origin._sourceValue;
- _sourceVValues = origin._sourceVValues;
+ _sourceValues = origin._sourceValues;
_contentUid = origin._contentUid;
_content = content;
@@ -143,22 +148,19 @@ namespace Umbraco.Web.PublishedCache.NuCache
if (languageId == null && segment == null)
{
if (_interInitialized) return _interValue;
-
_interValue = PropertyType.ConvertSourceToInter(_content, _sourceValue, _isPreviewing);
_interInitialized = true;
return _interValue;
}
- if (_sourceVValues == null)
- _sourceVValues = new List();
+ if (_sourceValues == null)
+ _sourceValues = new Dictionary();
- var vvalue = _sourceVValues.FirstOrDefault(x => x.LanguageId == languageId && x.Segment == segment);
-
- if (vvalue == null)
- _sourceVValues.Add(vvalue = new PropertyValue { LanguageId = languageId, Segment = segment });
+ var k = new CompositeIntStringKey(languageId, segment);
+ if (!_sourceValues.TryGetValue(k, out var vvalue))
+ _sourceValues[k] = vvalue = new SourceInterValue { LanguageId = languageId, Segment = segment };
if (vvalue.InterInitialized) return vvalue.InterValue;
-
vvalue.InterValue = PropertyType.ConvertSourceToInter(_content, vvalue.SourceValue, _isPreviewing);
vvalue.InterInitialized = true;
return vvalue.InterValue;
@@ -169,13 +171,11 @@ namespace Umbraco.Web.PublishedCache.NuCache
if (languageId == null && segment == null)
return _sourceValue;
- List vvalues;
lock (_locko)
{
- vvalues = _sourceVValues;
+ if (_sourceValues == null) return null;
+ return _sourceValues.TryGetValue(new CompositeIntStringKey(languageId, segment), out var sourceValue) ? sourceValue.SourceValue : null;
}
-
- return vvalues?.FirstOrDefault(x => x.LanguageId == languageId && x.Segment == segment);
}
public override object GetValue(int? languageId = null, string segment = null)
@@ -183,10 +183,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
lock (_locko)
{
var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(languageId, segment);
- if (cacheValues.ObjectInitialized) return cacheValues.ObjectValue;
// initial reference cache level always is .Content
- cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Element, GetInterValue(languageId, segment), _isPreviewing);
+ const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
+
+ if (cacheValues.ObjectInitialized) return cacheValues.ObjectValue;
+ cacheValues.ObjectValue = PropertyType.ConvertInterToObject(_content, initialCacheLevel, GetInterValue(languageId, segment), _isPreviewing);
cacheValues.ObjectInitialized = true;
return cacheValues.ObjectValue;
}
@@ -197,10 +199,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
lock (_locko)
{
var cacheValues = GetCacheValues(PropertyType.CacheLevel).For(languageId, segment);
- if (cacheValues.XPathInitialized) return cacheValues.XPathValue;
// initial reference cache level always is .Content
- cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, PropertyCacheLevel.Element, GetInterValue(languageId, segment), _isPreviewing);
+ const PropertyCacheLevel initialCacheLevel = PropertyCacheLevel.Element;
+
+ if (cacheValues.XPathInitialized) return cacheValues.XPathValue;
+ cacheValues.XPathValue = PropertyType.ConvertInterToXPath(_content, initialCacheLevel, GetInterValue(languageId, segment), _isPreviewing);
cacheValues.XPathInitialized = true;
return cacheValues.XPathValue;
}
@@ -208,40 +212,45 @@ namespace Umbraco.Web.PublishedCache.NuCache
#region Classes
- private class CacheValues
+ private class CacheValue
{
- private int? _languageId;
- private string _segment;
+ public bool ObjectInitialized { get; set; }
+ public object ObjectValue { get; set; }
+ public bool XPathInitialized { get; set; }
+ public object XPathValue { get; set; }
+ }
- public bool ObjectInitialized;
- public object ObjectValue;
- public bool XPathInitialized;
- public object XPathValue;
-
- private List _values;
+ private class CacheValues : CacheValue
+ {
+ private Dictionary _values;
// this is always invoked from within a lock, so does not require its own lock
- public CacheValues For(int? languageId, string segment)
+ public CacheValue For(int? languageId, string segment)
{
if (languageId == null && segment == null)
return this;
if (_values == null)
- _values = new List();
+ _values = new Dictionary();
- var values = _values.FirstOrDefault(x => x._languageId == languageId && x._segment == segment);
+ var k = new CompositeIntStringKey(languageId, segment);
+ if (!_values.TryGetValue(k, out var value))
+ _values[k] = value = new CacheValue();
- if (values == null)
- _values.Add(values = new CacheValues { _languageId = languageId, _segment = segment });
-
- return values;
+ return value;
}
}
- private class PropertyValue
+ private class SourceInterValue
{
+ private string _segment;
+
public int? LanguageId { get; set; }
- public string Segment { get; set; }
+ public string Segment
+ {
+ get => _segment;
+ internal set => _segment = value?.ToLowerInvariant();
+ }
public object SourceValue { get; set; }
public bool InterInitialized { get; set; }
public object InterValue { get; set; }