diff --git a/src/Umbraco.Core/Collections/CompositeNStringNStringKey.cs b/src/Umbraco.Core/Collections/CompositeNStringNStringKey.cs new file mode 100644 index 0000000000..a06696f6c6 --- /dev/null +++ b/src/Umbraco.Core/Collections/CompositeNStringNStringKey.cs @@ -0,0 +1,41 @@ +using System; + +namespace Umbraco.Core.Collections +{ + /// + /// Represents a composite key of (string, string) for fast dictionaries. + /// + /// + /// The string parts of the key are case-insensitive. + /// Null is a valid value for both parts. + /// + public struct CompositeNStringNStringKey : IEquatable + { + private readonly string _key1; + private readonly string _key2; + + /// + /// Initializes a new instance of the struct. + /// + public CompositeNStringNStringKey(string key1, string key2) + { + _key1 = key1?.ToLowerInvariant() ?? "NULL"; + _key2 = key2?.ToLowerInvariant() ?? "NULL"; + } + + public bool Equals(CompositeNStringNStringKey other) + => _key2 == other._key2 && _key1 == other._key1; + + public override bool Equals(object obj) + => obj is CompositeNStringNStringKey other && _key2 == other._key2 && _key1 == other._key1; + + public override int GetHashCode() + => _key2.GetHashCode() * 31 + _key1.GetHashCode(); + + public static bool operator ==(CompositeNStringNStringKey key1, CompositeNStringNStringKey key2) + => key1._key2 == key2._key2 && key1._key1 == key2._key1; + + public static bool operator !=(CompositeNStringNStringKey key1, CompositeNStringNStringKey key2) + => key1._key2 != key2._key2 || key1._key1 != key2._key1; + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Collections/CompositeStringStringKey.cs b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs index 78ee9b0d2a..c053b08a22 100644 --- a/src/Umbraco.Core/Collections/CompositeStringStringKey.cs +++ b/src/Umbraco.Core/Collections/CompositeStringStringKey.cs @@ -7,7 +7,7 @@ namespace Umbraco.Core.Collections /// /// /// The string parts of the key are case-insensitive. - /// Null is a valid value for both parts. + /// Null is NOT a valid value for neither parts. /// public struct CompositeStringStringKey : IEquatable { @@ -19,12 +19,8 @@ namespace Umbraco.Core.Collections /// public CompositeStringStringKey(string key1, string key2) { - // fixme temp - debugging - if (key1 == null) throw new Exception("Getting null culture in CompositeStringStringKey constructor, fix!"); - if (key2 == null) throw new Exception("Getting null segment in CompositeStringStringKey constructor, fix!"); - - _key1 = key1?.ToLowerInvariant() ?? "NULL"; - _key2 = key2?.ToLowerInvariant() ?? "NULL"; + _key1 = key1?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(key1)); + _key2 = key2?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(key2)); } public bool Equals(CompositeStringStringKey other) diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 7e53eb764a..19a63925df 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Models { private List _values = new List(); private PropertyValue _pvalue; - private Dictionary _vvalues; + private Dictionary _vvalues; private static readonly Lazy Ps = new Lazy(); @@ -77,8 +77,8 @@ namespace Umbraco.Core.Models // custom comparer for enumerable // ReSharper disable once MergeCastWithTypeCheck - if (o is IEnumerable && o1 is IEnumerable) - return ((IEnumerable) o).Cast().UnsortedSequenceEqual(((IEnumerable) o1).Cast()); + if (o is IEnumerable && o1 is IEnumerable enumerable) + return ((IEnumerable) o).Cast().UnsortedSequenceEqual(enumerable.Cast()); return o.Equals(o1); }, o => o.GetHashCode()); @@ -104,7 +104,7 @@ namespace Umbraco.Core.Models _values = value.Where(x => PropertyType.ValidateVariation(x.Culture, x.Segment, false)).ToList(); _pvalue = _values.FirstOrDefault(x => x.Culture == null && x.Segment == null); _vvalues = _values.Count > (_pvalue == null ? 0 : 1) - ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeStringStringKey(x.Culture, x.Segment), x => x) + ? _values.Where(x => x != _pvalue).ToDictionary(x => new CompositeNStringNStringKey(x.Culture, x.Segment), x => x) : null; } } @@ -138,7 +138,7 @@ namespace Umbraco.Core.Models if (!PropertyType.ValidateVariation(culture, segment, false)) return null; if (culture == null && segment == null) return GetPropertyValue(_pvalue, published); if (_vvalues == null) return null; - return _vvalues.TryGetValue(new CompositeStringStringKey(culture, segment), out var pvalue) + return _vvalues.TryGetValue(new CompositeNStringNStringKey(culture, segment), out var pvalue) ? GetPropertyValue(pvalue, published) : null; } @@ -177,7 +177,7 @@ namespace Umbraco.Core.Models { PropertyType.ValidateVariation(culture, segment, true); - (var pvalue, _) = GetPValue(culture, segment, false); + var (pvalue, _) = GetPValue(culture, segment, false); if (pvalue == null) return; PublishPropertyValue(pvalue); } @@ -222,7 +222,7 @@ namespace Umbraco.Core.Models internal void ClearPublishedValue(string culture = null, string segment = null) { PropertyType.ValidateVariation(culture, segment, true); - (var pvalue, _) = GetPValue(culture, segment, false); + var (pvalue, _) = GetPValue(culture, segment, false); if (pvalue == null) return; ClearPublishedPropertyValue(pvalue); } @@ -271,12 +271,8 @@ namespace Umbraco.Core.Models /// public void SetValue(object value, string culture = null, string segment = null) { - if (PropertyType.Variations == ContentVariation.InvariantNeutral) - { - culture = null; - } PropertyType.ValidateVariation(culture, segment, true); - (var pvalue, var change) = GetPValue(culture, segment, true); + var (pvalue, change) = GetPValue(culture, segment, true); var origValue = pvalue.EditedValue; var setValue = PropertyType.ConvertAssignedValue(value); @@ -289,7 +285,7 @@ namespace Umbraco.Core.Models // bypasses all changes detection and is the *only* way to set the published value internal void FactorySetValue(string culture, string segment, bool published, object value) { - (var pvalue, _) = GetPValue(culture, segment, true); + var (pvalue, _) = GetPValue(culture, segment, true); if (published && PropertyType.IsPublishing) pvalue.PublishedValue = value; @@ -319,10 +315,10 @@ namespace Umbraco.Core.Models if (_vvalues == null) { if (!create) return (null, false); - _vvalues = new Dictionary(); + _vvalues = new Dictionary(); change = true; } - var k = new CompositeStringStringKey(culture, segment); + var k = new CompositeNStringNStringKey(culture, segment); if (!_vvalues.TryGetValue(k, out var pvalue)) { if (!create) return (null, false); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 384f1defa6..10d9277ccf 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -136,6 +136,7 @@ + diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index 5cb59fecbe..205507cce8 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -499,9 +499,10 @@ namespace Umbraco.Tests.Services for (int i = 0; i < entities.Length; i++) { + Assert.AreEqual(0, entities[i].AdditionalData.Count); + if (i % 2 == 0) { - Assert.AreEqual(1, entities[i].AdditionalData.Count); var doc = (IDocumentEntitySlim)entities[i]; var keys = doc.CultureNames.Keys.ToList(); var vals = doc.CultureNames.Values.ToList(); diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs index aec5c375d9..b5070ae8ad 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs @@ -53,11 +53,6 @@ namespace Umbraco.Web.Models.Mapping //a language Id needs to be set for a property type that can be varried by language throw new InvalidOperationException($"No languageId found in mapping operation when one is required for the culture neutral property type {property.PropertyType.Alias}"); } - if (property.PropertyType.Variations == ContentVariation.InvariantNeutral) - { - culture = null; - - } var result = new TDestination {