From a5adb322f1431bc1b6ed736b71100a704787ab0d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 10 Jun 2020 16:12:00 +1000 Subject: [PATCH] Updates the caching layer to handle GUID keys for content types while preserving backwards compat, fixes unit tests, removes the strongly typed lists for the block editor value since it's unecessary --- .../PublishedContent/IPublishedContentType.cs | 16 ++- .../IPublishedContentTypeFactory.cs | 1 + .../IPublishedModelFactory.cs | 1 + .../PublishedContentExtensionsForModels.cs | 1 + .../PublishedContent/PublishedContentType.cs | 35 +++++- .../PublishedContentTypeExtensions.cs | 24 ++++ .../PublishedContentTypeFactory.cs | 8 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PublishedMediaCacheTests.cs | 6 +- .../PublishedContentCache.cs | 14 +-- .../PublishedMediaCache.cs | 14 +-- .../BlockListPropertyValueConverterTests.cs | 47 ++++---- .../PropertyEditorValueConverterTests.cs | 2 +- .../Published/ConvertersTests.cs | 42 +++---- .../Published/NestedContentTests.cs | 6 +- .../Published/PropertyCacheLevelTests.cs | 6 +- .../PublishedContentDataTableTests.cs | 4 +- .../PublishedContentLanguageVariantTests.cs | 4 +- .../PublishedContentMoreTests.cs | 7 +- .../PublishedContentTestBase.cs | 3 +- .../PublishedContent/PublishedContentTests.cs | 16 +-- .../PublishedContent/PublishedRouterTests.cs | 2 +- .../SolidPublishedSnapshot.cs | 31 +++--- .../Routing/ContentFinderByAliasTests.cs | 5 +- .../ContentFinderByAliasWithDomainsTests.cs | 5 +- .../Routing/MediaUrlProviderTests.cs | 4 +- .../Routing/RenderRouteHandlerTests.cs | 2 +- src/Umbraco.Tests/Routing/UrlProviderTests.cs | 6 +- .../Templates/HtmlImageSourceParserTests.cs | 2 +- .../Templates/HtmlLocalLinkParserTests.cs | 4 +- src/Umbraco.Tests/TestHelpers/BaseWebTest.cs | 2 +- .../Testing/TestingTests/MockTests.cs | 2 +- .../PropertyEditors/BlockListConfiguration.cs | 3 +- .../ValueConverters/BlockEditorConverter.cs | 12 +- .../BlockListPropertyValueConverter.cs | 17 ++- .../PublishedCache/IPublishedCache.cs | 10 ++ .../PublishedCache/IPublishedContentCache.cs | 5 + .../PublishedCache/IPublishedMediaCache.cs | 5 + .../PublishedCache/NuCache/ContentCache.cs | 14 +-- .../PublishedCache/NuCache/ContentStore.cs | 81 +++++++++----- .../PublishedCache/NuCache/MediaCache.cs | 14 +-- .../PublishedCache/PublishedCacheBase.cs | 4 +- .../PublishedContentTypeCache.cs | 105 ++++++++++++------ src/umbraco.sln | 23 +++- 44 files changed, 392 insertions(+), 224 deletions(-) create mode 100644 src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeExtensions.cs diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs index ab6920377c..cfc789324a 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentType.cs @@ -1,7 +1,21 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace Umbraco.Core.Models.PublishedContent { + /// + /// Represents an type. + /// + /// Instances implementing the interface should be + /// immutable, ie if the content type changes, then a new instance needs to be created. + public interface IPublishedContentType2 : IPublishedContentType + { + /// + /// Gets the unique key for the content type. + /// + Guid Key { get; } + } + /// /// Represents an type. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs index 89009ac7b8..1a29f970d9 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedContentTypeFactory.cs @@ -1,5 +1,6 @@ namespace Umbraco. Core.Models.PublishedContent { + /// /// Creates published content types. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs index ae4caf352e..60fa0fe603 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs @@ -3,6 +3,7 @@ using System.Collections; namespace Umbraco.Core.Models.PublishedContent { + /// /// Provides the published model creation service. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs index bfc65b70d6..033396e4a1 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentExtensionsForModels.cs @@ -3,6 +3,7 @@ using Umbraco.Core.Composing; namespace Umbraco.Core.Models.PublishedContent { + /// /// Provides strongly typed published content models services. /// diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 458b63ade3..7aa9b0dfd9 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Instances of the class are immutable, ie /// if the content type changes, then a new class needs to be created. - public class PublishedContentType : IPublishedContentType + public class PublishedContentType : IPublishedContentType2 { private readonly IPublishedPropertyType[] _propertyTypes; @@ -20,7 +20,7 @@ namespace Umbraco.Core.Models.PublishedContent /// Initializes a new instance of the class with a content type. /// public PublishedContentType(IContentTypeComposition contentType, IPublishedContentTypeFactory factory) - : this(contentType.Id, contentType.Alias, contentType.GetItemType(), contentType.CompositionAliases(), contentType.Variations, contentType.IsElement) + : this(contentType.Key, contentType.Id, contentType.Alias, contentType.GetItemType(), contentType.CompositionAliases(), contentType.Variations, contentType.IsElement) { var propertyTypes = contentType.CompositionPropertyTypes .Select(x => factory.CreatePropertyType(this, x)) @@ -40,8 +40,20 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Values are assumed to be consistent and are not checked. /// + public PublishedContentType(Guid key, int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations, bool isElement = false) + : this(key, id, alias, itemType, compositionAliases, variations, isElement) + { + var propertyTypesA = propertyTypes.ToArray(); + foreach (var propertyType in propertyTypesA) + propertyType.ContentType = this; + _propertyTypes = propertyTypesA; + + InitializeIndexes(); + } + + [Obsolete("Use the overload specifying a key instead")] public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, IEnumerable propertyTypes, ContentVariation variations, bool isElement = false) - : this (id, alias, itemType, compositionAliases, variations, isElement) + : this (Guid.Empty, id, alias, itemType, compositionAliases, variations, isElement) { var propertyTypesA = propertyTypes.ToArray(); foreach (var propertyType in propertyTypesA) @@ -57,16 +69,26 @@ namespace Umbraco.Core.Models.PublishedContent /// /// Values are assumed to be consistent and are not checked. /// + public PublishedContentType(Guid key, int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations, bool isElement = false) + : this(key, id, alias, itemType, compositionAliases, variations, isElement) + { + _propertyTypes = propertyTypes(this).ToArray(); + + InitializeIndexes(); + } + + [Obsolete("Use the overload specifying a key instead")] public PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations, bool isElement = false) - : this(id, alias, itemType, compositionAliases, variations, isElement) + : this(Guid.Empty, id, alias, itemType, compositionAliases, variations, isElement) { _propertyTypes = propertyTypes(this).ToArray(); InitializeIndexes(); } - private PublishedContentType(int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, ContentVariation variations, bool isElement) + private PublishedContentType(Guid key, int id, string alias, PublishedItemType itemType, IEnumerable compositionAliases, ContentVariation variations, bool isElement) { + Key = key; Id = id; Alias = alias; ItemType = itemType; @@ -116,6 +138,9 @@ namespace Umbraco.Core.Models.PublishedContent #region Content type + /// + public Guid Key { get; } + /// public int Id { get; } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeExtensions.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeExtensions.cs new file mode 100644 index 0000000000..feab33c1d6 --- /dev/null +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeExtensions.cs @@ -0,0 +1,24 @@ +using System; + +namespace Umbraco.Core.Models.PublishedContent +{ + public static class PublishedContentTypeExtensions + { + /// + /// Get the GUID key from an + /// + /// + /// + /// + public static bool TryGetKey(this IPublishedContentType publishedContentType, out Guid key) + { + if (publishedContentType is IPublishedContentType2 contentTypeWithKey) + { + key = contentTypeWithKey.Key; + return true; + } + key = Guid.Empty; + return false; + } + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs index 34094508c3..c1548d3c3d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentTypeFactory.cs @@ -35,18 +35,18 @@ namespace Umbraco.Core.Models.PublishedContent /// This method is for tests and is not intended to be used directly from application code. /// /// Values are assumed to be consisted and are not checked. - internal IPublishedContentType CreateContentType(int id, string alias, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) + internal IPublishedContentType CreateContentType(Guid key, int id, string alias, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { - return new PublishedContentType(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); + return new PublishedContentType(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, variations, isElement); } /// /// This method is for tests and is not intended to be used directly from application code. /// /// Values are assumed to be consisted and are not checked. - internal IPublishedContentType CreateContentType(int id, string alias, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) + internal IPublishedContentType CreateContentType(Guid key, int id, string alias, IEnumerable compositionAliases, Func> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false) { - return new PublishedContentType(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); + return new PublishedContentType(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement); } /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 809074073b..408abed4e0 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -139,6 +139,7 @@ + diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index f3d9f895ef..2346740ffb 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -42,9 +42,9 @@ namespace Umbraco.Tests.Cache.PublishedCache protected override void Initialize() { base.Initialize(); - var type = new AutoPublishedContentType(22, "myType", new PublishedPropertyType[] { }); - var image = new AutoPublishedContentType(23, "Image", new PublishedPropertyType[] { }); - var testMediaType = new AutoPublishedContentType(24, "TestMediaType", new PublishedPropertyType[] { }); + var type = new AutoPublishedContentType(Guid.NewGuid(), 22, "myType", new PublishedPropertyType[] { }); + var image = new AutoPublishedContentType(Guid.NewGuid(), 23, "Image", new PublishedPropertyType[] { }); + var testMediaType = new AutoPublishedContentType(Guid.NewGuid(), 24, "TestMediaType", new PublishedPropertyType[] { }); _mediaTypes = new Dictionary { { type.Alias, type }, diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs index 8ce6b10983..48f0e7b27e 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs @@ -14,7 +14,7 @@ using Umbraco.Web.Routing; namespace Umbraco.Tests.LegacyXmlPublishedCache { - internal class PublishedContentCache : PublishedCacheBase, IPublishedContentCache + internal class PublishedContentCache : PublishedCacheBase, IPublishedContentCache2 { private readonly IAppCache _appCache; private readonly IGlobalSettings _globalSettings; @@ -532,15 +532,11 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache #region Content types - public override IPublishedContentType GetContentType(int id) - { - return _contentTypeCache.Get(PublishedItemType.Content, id); - } + public override IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Content, id); - public override IPublishedContentType GetContentType(string alias) - { - return _contentTypeCache.Get(PublishedItemType.Content, alias); - } + public override IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Content, alias); + + public override IPublishedContentType GetContentType(Guid key) => _contentTypeCache.Get(PublishedItemType.Content, key); #endregion } diff --git a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs index 999d7f040d..56033e6b0a 100644 --- a/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache /// /// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly. /// - internal class PublishedMediaCache : PublishedCacheBase, IPublishedMediaCache + internal class PublishedMediaCache : PublishedCacheBase, IPublishedMediaCache2 { private readonly IMediaService _mediaService; private readonly IUserService _userService; @@ -612,15 +612,11 @@ namespace Umbraco.Tests.LegacyXmlPublishedCache #region Content types - public override IPublishedContentType GetContentType(int id) - { - return _contentTypeCache.Get(PublishedItemType.Media, id); - } + public override IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Media, id); - public override IPublishedContentType GetContentType(string alias) - { - return _contentTypeCache.Get(PublishedItemType.Media, alias); - } + public override IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Media, alias); + + public override IPublishedContentType GetContentType(Guid key) => _contentTypeCache.Get(PublishedItemType.Media, key); public override IEnumerable GetByContentType(IPublishedContentType contentType) { diff --git a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs index 6a7ec33a5a..655db4e337 100644 --- a/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/BlockListPropertyValueConverterTests.cs @@ -18,21 +18,28 @@ namespace Umbraco.Tests.PropertyEditors [TestFixture] public class BlockListPropertyValueConverterTests { + private readonly Guid Key1 = Guid.NewGuid(); + private readonly Guid Key2 = Guid.NewGuid(); + private readonly string Alias1 = "Test1"; + private readonly string Alias2 = "Test2"; + /// /// Setup mocks for IPublishedSnapshotAccessor /// /// private IPublishedSnapshotAccessor GetPublishedSnapshotAccessor() { - var test1ContentType = Mock.Of(x => + var test1ContentType = Mock.Of(x => x.IsElement == true - && x.Alias == "Test1"); - var test2ContentType = Mock.Of(x => + && x.Key == Key1 + && x.Alias == Alias1); + var test2ContentType = Mock.Of(x => x.IsElement == true - && x.Alias == "Test2"); - var contentCache = new Mock(); - contentCache.Setup(x => x.GetContentType("Test1")).Returns(test1ContentType); - contentCache.Setup(x => x.GetContentType("Test2")).Returns(test2ContentType); + && x.Key == Key2 + && x.Alias == Alias2); + var contentCache = new Mock(); + contentCache.Setup(x => x.GetContentType(Key1)).Returns(test1ContentType); + contentCache.Setup(x => x.GetContentType(Key2)).Returns(test2ContentType); var publishedSnapshot = Mock.Of(x => x.Content == contentCache.Object); var publishedSnapshotAccessor = Mock.Of(x => x.PublishedSnapshot == publishedSnapshot); return publishedSnapshotAccessor; @@ -44,7 +51,6 @@ namespace Umbraco.Tests.PropertyEditors var publishedModelFactory = new NoopPublishedModelFactory(); var editor = new BlockListPropertyValueConverter( Mock.Of(), - publishedModelFactory, new BlockEditorConverter(publishedSnapshotAccessor, publishedModelFactory)); return editor; } @@ -54,11 +60,11 @@ namespace Umbraco.Tests.PropertyEditors Blocks = new[] { new BlockListConfiguration.BlockConfiguration { - Alias = "Test1" + Key = Key1 }, new BlockListConfiguration.BlockConfiguration { - Alias = "Test2" + Key = Key2 } } }; @@ -68,7 +74,7 @@ namespace Umbraco.Tests.PropertyEditors Blocks = new[] { new BlockListConfiguration.BlockConfiguration { - Alias = "Test1" + Key = Key1 } } }; @@ -101,7 +107,8 @@ namespace Umbraco.Tests.PropertyEditors var valueType = editor.GetPropertyValueType(propType); - Assert.AreEqual(typeof(IEnumerable), valueType); + // the result is always block list model + Assert.AreEqual(typeof(BlockListModel), valueType); } [Test] @@ -115,10 +122,8 @@ namespace Umbraco.Tests.PropertyEditors var valueType = editor.GetPropertyValueType(propType); - var modelType = typeof(IEnumerable<>).MakeGenericType(ModelType.For(config.Blocks[0].Alias)); - - // we can't compare the exact match of types because ModelType.For generates a new/different type even if the same alias is used - Assert.AreEqual(modelType.FullName, valueType.FullName); + // the result is always block list model + Assert.AreEqual(typeof(BlockListModel), valueType); } [Test] @@ -225,7 +230,7 @@ data: []}"; }, data: [ { - 'contentTypeAlias': 'home', + 'contentTypeKey': '" + Key1 + @"', 'key': '1304E1DD-0000-4396-84FE-8A399231CB3D' } ] @@ -258,7 +263,7 @@ data: []}"; }, data: [ { - 'contentTypeAlias': 'Test1', + 'contentTypeKey': '" + Key1 + @"', 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' } ] @@ -300,15 +305,15 @@ data: []}"; }, data: [ { - 'contentTypeAlias': 'Test1', + 'contentTypeKey': '" + Key1 + @"', 'udi': 'umb://element/1304E1DDAC87439684FE8A399231CB3D' }, { - 'contentTypeAlias': 'Test2', + 'contentTypeKey': '" + Key2 + @"', 'udi': 'umb://element/E05A034704424AB3A520E048E6197E79' }, { - 'contentTypeAlias': 'Test2', + 'contentTypeKey': '" + Key2 + @"', 'udi': 'umb://element/0A4A416E547D464FABCC6F345C17809A' } ] diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs index 43c1a83d33..a9e3e8b9db 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs @@ -93,7 +93,7 @@ namespace Umbraco.Tests.PropertyEditors }))); var publishedPropType = new PublishedPropertyType( - new PublishedContentType(1234, "test", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing), + new PublishedContentType(Guid.NewGuid(), 1234, "test", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing), new PropertyType("test", ValueStorageType.Nvarchar) { DataTypeId = 123 }, new PropertyValueConverterCollection(Enumerable.Empty()), Mock.Of(), mockPublishedContentTypeFactory.Object); diff --git a/src/Umbraco.Tests/Published/ConvertersTests.cs b/src/Umbraco.Tests/Published/ConvertersTests.cs index 671129848c..3c60f4ddd0 100644 --- a/src/Umbraco.Tests/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests/Published/ConvertersTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.Published yield return contentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", CreatePropertyTypes); + var elementType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "element1", CreatePropertyTypes); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); @@ -74,7 +74,7 @@ namespace Umbraco.Tests.Published => propertyType.EditorAlias.InvariantEquals("Umbraco.Void"); public Type GetPropertyValueType(IPublishedPropertyType propertyType) - => typeof (int); + => typeof(int); public PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; @@ -83,10 +83,10 @@ namespace Umbraco.Tests.Published => int.TryParse(source as string, out int i) ? i : 0; public object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) - => (int) inter; + => (int)inter; public object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) - => ((int) inter).ToString(); + => ((int)inter).ToString(); } #endregion @@ -120,11 +120,11 @@ namespace Umbraco.Tests.Published yield return contentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", CreatePropertyTypes); + var elementType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "element1", CreatePropertyTypes); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "1234" } }, false); - var cntType1 = contentTypeFactory.CreateContentType(1001, "cnt1", t => Enumerable.Empty()); + var cntType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1001, "cnt1", t => Enumerable.Empty()); var cnt1 = new SolidPublishedContent(cntType1) { Id = 1234 }; cacheContent[cnt1.Id] = cnt1; @@ -143,7 +143,7 @@ namespace Umbraco.Tests.Published } public bool? IsValue(object value, PropertyValueLevel level) - => value != null && (!(value is string) || string.IsNullOrWhiteSpace((string) value) == false); + => value != null && (!(value is string) || string.IsNullOrWhiteSpace((string)value) == false); public bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.InvariantEquals("Umbraco.Void"); @@ -162,10 +162,10 @@ namespace Umbraco.Tests.Published => int.TryParse(source as string, out int i) ? i : -1; public object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) - => _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById((int) inter); + => _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById((int)inter); public object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) - => ((int) inter).ToString(); + => ((int)inter).ToString(); } #endregion @@ -215,10 +215,10 @@ namespace Umbraco.Tests.Published yield return contentTypeFactory.CreatePropertyType(contentType, "prop" + i, i); } - var elementType1 = contentTypeFactory.CreateContentType(1000, "element1", t => CreatePropertyTypes(t, 1)); - var elementType2 = contentTypeFactory.CreateContentType(1001, "element2", t => CreatePropertyTypes(t, 2)); - var contentType1 = contentTypeFactory.CreateContentType(1002, "content1", t => CreatePropertyTypes(t, 1)); - var contentType2 = contentTypeFactory.CreateContentType(1003, "content2", t => CreatePropertyTypes(t, 2)); + var elementType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "element1", t => CreatePropertyTypes(t, 1)); + var elementType2 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1001, "element2", t => CreatePropertyTypes(t, 2)); + var contentType1 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1002, "content1", t => CreatePropertyTypes(t, 1)); + var contentType2 = contentTypeFactory.CreateContentType(Guid.NewGuid(), 1003, "content2", t => CreatePropertyTypes(t, 2)); var element1 = new PublishedElement(elementType1, Guid.NewGuid(), new Dictionary { { "prop1", "val1" } }, false); var element2 = new PublishedElement(elementType2, Guid.NewGuid(), new Dictionary { { "prop2", "1003" } }, false); @@ -239,22 +239,22 @@ namespace Umbraco.Tests.Published // can get the actual property Clr type // ie ModelType gets properly mapped by IPublishedContentModelFactory // must test ModelClrType with special equals 'cos they are not ref-equals - Assert.IsTrue(ModelType.Equals(typeof (IEnumerable<>).MakeGenericType(ModelType.For("content1")), contentType2.GetPropertyType("prop2").ModelClrType)); - Assert.AreEqual(typeof (IEnumerable), contentType2.GetPropertyType("prop2").ClrType); + Assert.IsTrue(ModelType.Equals(typeof(IEnumerable<>).MakeGenericType(ModelType.For("content1")), contentType2.GetPropertyType("prop2").ModelClrType)); + Assert.AreEqual(typeof(IEnumerable), contentType2.GetPropertyType("prop2").ClrType); // can create a model for an element var model1 = factory.CreateModel(element1); Assert.IsInstanceOf(model1); - Assert.AreEqual("val1", ((PublishedSnapshotTestObjects.TestElementModel1) model1).Prop1); + Assert.AreEqual("val1", ((PublishedSnapshotTestObjects.TestElementModel1)model1).Prop1); // can create a model for a published content var model2 = factory.CreateModel(element2); Assert.IsInstanceOf(model2); - var mmodel2 = (PublishedSnapshotTestObjects.TestElementModel2) model2; + var mmodel2 = (PublishedSnapshotTestObjects.TestElementModel2)model2; // and get direct property Assert.IsInstanceOf(model2.Value("prop2")); - Assert.AreEqual(1, ((PublishedSnapshotTestObjects.TestContentModel1[]) model2.Value("prop2")).Length); + Assert.AreEqual(1, ((PublishedSnapshotTestObjects.TestContentModel1[])model2.Value("prop2")).Length); // and get model property Assert.IsInstanceOf>(mmodel2.Prop2); @@ -271,7 +271,7 @@ namespace Umbraco.Tests.Published => propertyType.EditorAlias == "Umbraco.Void"; public override Type GetPropertyValueType(IPublishedPropertyType propertyType) - => typeof (string); + => typeof(string); public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element; @@ -290,7 +290,7 @@ namespace Umbraco.Tests.Published => propertyType.EditorAlias == "Umbraco.Void.2"; public override Type GetPropertyValueType(IPublishedPropertyType propertyType) - => typeof (IEnumerable<>).MakeGenericType(ModelType.For("content1")); + => typeof(IEnumerable<>).MakeGenericType(ModelType.For("content1")); public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Elements; @@ -303,7 +303,7 @@ namespace Umbraco.Tests.Published public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { - return ((int[]) inter).Select(x => (PublishedSnapshotTestObjects.TestContentModel1) _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(x)).ToArray(); + return ((int[])inter).Select(x => (PublishedSnapshotTestObjects.TestContentModel1)_publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(x)).ToArray(); } } diff --git a/src/Umbraco.Tests/Published/NestedContentTests.cs b/src/Umbraco.Tests/Published/NestedContentTests.cs index a102b9f93e..70da652a9a 100644 --- a/src/Umbraco.Tests/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests/Published/NestedContentTests.cs @@ -144,9 +144,9 @@ namespace Umbraco.Tests.Published yield return factory.CreatePropertyType(contentType, "propertyN1", 3); } - var contentType1 = factory.CreateContentType(1, "content1", CreatePropertyTypes1); - var contentType2 = factory.CreateContentType(2, "content2", CreatePropertyTypes2); - var contentTypeN1 = factory.CreateContentType(2, "contentN1", CreatePropertyTypesN1, isElement: true); + var contentType1 = factory.CreateContentType(Guid.NewGuid(), 1, "content1", CreatePropertyTypes1); + var contentType2 = factory.CreateContentType(Guid.NewGuid(), 2, "content2", CreatePropertyTypes2); + var contentTypeN1 = factory.CreateContentType(Guid.NewGuid(), 2, "contentN1", CreatePropertyTypesN1, isElement: true); // mocked content cache returns content types contentCache diff --git a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs index 9db539d142..a795ca433e 100644 --- a/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests/Published/PropertyCacheLevelTests.cs @@ -41,7 +41,7 @@ namespace Umbraco.Tests.Published yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); + var setType1 = publishedContentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "set1", CreatePropertyTypes); // PublishedElementPropertyBase.GetCacheLevels: // @@ -122,7 +122,7 @@ namespace Umbraco.Tests.Published yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); + var setType1 = publishedContentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "set1", CreatePropertyTypes); var elementsCache = new FastDictionaryAppCache(); var snapshotCache = new FastDictionaryAppCache(); @@ -199,7 +199,7 @@ namespace Umbraco.Tests.Published yield return publishedContentTypeFactory.CreatePropertyType(contentType, "prop1", 1); } - var setType1 = publishedContentTypeFactory.CreateContentType(1000, "set1", CreatePropertyTypes); + var setType1 = publishedContentTypeFactory.CreateContentType(Guid.NewGuid(), 1000, "set1", CreatePropertyTypes); Assert.Throws(() => { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index cc455b8e5d..7277f75be2 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -98,7 +98,7 @@ namespace Umbraco.Tests.PublishedContent var doc = GetContent(true, 1); //change a doc type alias var c = (SolidPublishedContent)doc.Children.ElementAt(0); - c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + c.ContentType = new PublishedContentType(Guid.NewGuid(), 22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var dt = doc.ChildrenAsTable(Current.Services, "Child"); @@ -129,7 +129,7 @@ namespace Umbraco.Tests.PublishedContent var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); var contentTypeAlias = createChildren ? "Parent" : "Child"; - var contentType = new PublishedContentType(22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var contentType = new PublishedContentType(Guid.NewGuid(), 22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var d = new SolidPublishedContent(contentType) { CreateDate = DateTime.Now, diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs index 62447742ff..636f8502ed 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs @@ -73,14 +73,14 @@ namespace Umbraco.Tests.PublishedContent yield return factory.CreatePropertyType(contentType, "noprop", 1, variations: ContentVariation.Culture); } - var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes1); + var contentType1 = factory.CreateContentType(Guid.NewGuid(), 1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes1); IEnumerable CreatePropertyTypes2(IPublishedContentType contentType) { yield return factory.CreatePropertyType(contentType, "prop3", 1, variations: ContentVariation.Culture); } - var contentType2 = factory.CreateContentType(2, "contentType2", Enumerable.Empty(), CreatePropertyTypes2); + var contentType2 = factory.CreateContentType(Guid.NewGuid(), 2, "contentType2", Enumerable.Empty(), CreatePropertyTypes2); var prop1 = new SolidPublishedPropertyWithLanguageVariants { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index 440474ae74..e2a1721d26 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -7,6 +7,7 @@ using Umbraco.Web; using Umbraco.Core; using Umbraco.Tests.Testing; using Umbraco.Web.Composing; +using System; namespace Umbraco.Tests.PublishedContent { @@ -21,9 +22,9 @@ namespace Umbraco.Tests.PublishedContent yield return factory.CreatePropertyType(contentType, "prop1", 1); } - var contentType1 = factory.CreateContentType(1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes); - var contentType2 = factory.CreateContentType(2, "ContentType2", Enumerable.Empty(), CreatePropertyTypes); - var contentType2Sub = factory.CreateContentType(3, "ContentType2Sub", Enumerable.Empty(), CreatePropertyTypes); + var contentType1 = factory.CreateContentType(Guid.NewGuid(), 1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes); + var contentType2 = factory.CreateContentType(Guid.NewGuid(), 2, "ContentType2", Enumerable.Empty(), CreatePropertyTypes); + var contentType2Sub = factory.CreateContentType(Guid.NewGuid(), 3, "ContentType2Sub", Enumerable.Empty(), CreatePropertyTypes); var content = new SolidPublishedContent(contentType1) { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index 59791fc645..7e0d0f332e 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Services; using Umbraco.Web; using Umbraco.Web.Templates; using Umbraco.Web.Models; +using System; namespace Umbraco.Tests.PublishedContent { @@ -56,7 +57,7 @@ namespace Umbraco.Tests.PublishedContent yield return publishedContentTypeFactory.CreatePropertyType(contentType, "content", 1); } - var type = new AutoPublishedContentType(0, "anything", CreatePropertyTypes); + var type = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", CreatePropertyTypes); ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; var umbracoContext = GetUmbracoContext("/test"); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 998fc92380..cafda161f4 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -83,8 +83,8 @@ namespace Umbraco.Tests.PublishedContent } var compositionAliases = new[] { "MyCompositionAlias" }; - var anythingType = new AutoPublishedContentType(0, "anything", compositionAliases, CreatePropertyTypes); - var homeType = new AutoPublishedContentType(0, "home", compositionAliases, CreatePropertyTypes); + var anythingType = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", compositionAliases, CreatePropertyTypes); + var homeType = new AutoPublishedContentType(Guid.NewGuid(), 0, "home", compositionAliases, CreatePropertyTypes); ContentTypesCache.GetPublishedContentTypeByAlias = alias => alias.InvariantEquals("home") ? homeType : anythingType; } @@ -398,8 +398,8 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_GroupBy_DocumentTypeAlias() { - var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { }); - var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { }); + var home = new AutoPublishedContentType(Guid.NewGuid(), 22, "Home", new PublishedPropertyType[] { }); + var custom = new AutoPublishedContentType(Guid.NewGuid(), 23, "CustomDocument", new PublishedPropertyType[] { }); var contentTypes = new Dictionary { { home.Alias, home }, @@ -419,8 +419,8 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_Where_DocumentTypeAlias() { - var home = new AutoPublishedContentType(22, "Home", new PublishedPropertyType[] { }); - var custom = new AutoPublishedContentType(23, "CustomDocument", new PublishedPropertyType[] { }); + var home = new AutoPublishedContentType(Guid.NewGuid(), 22, "Home", new PublishedPropertyType[] { }); + var custom = new AutoPublishedContentType(Guid.NewGuid(), 23, "CustomDocument", new PublishedPropertyType[] { }); var contentTypes = new Dictionary { { home.Alias, home }, @@ -903,7 +903,7 @@ namespace Umbraco.Tests.PublishedContent yield return factory.CreatePropertyType(contentType, "detached", 1003); } - var ct = factory.CreateContentType(0, "alias", CreatePropertyTypes); + var ct = factory.CreateContentType(Guid.NewGuid(), 0, "alias", CreatePropertyTypes); var pt = ct.GetPropertyType("detached"); var prop = new PublishedElementPropertyBase(pt, null, false, PropertyCacheLevel.None, 5548); Assert.IsInstanceOf(prop.GetValue()); @@ -935,7 +935,7 @@ namespace Umbraco.Tests.PublishedContent var guid = Guid.NewGuid(); - var ct = factory.CreateContentType(0, "alias", CreatePropertyTypes); + var ct = factory.CreateContentType(Guid.NewGuid(), 0, "alias", CreatePropertyTypes); var c = new ImageWithLegendModel(ct, guid, new Dictionary { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs index d8dbabb569..4a93fadbdf 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs @@ -66,7 +66,7 @@ namespace Umbraco.Tests.PublishedContent pc.Setup(content => content.Path).Returns("-1,1"); pc.Setup(content => content.Parent).Returns(() => null); pc.Setup(content => content.Properties).Returns(new Collection()); - pc.Setup(content => content.ContentType).Returns(new PublishedContentType(22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing)); + pc.Setup(content => content.ContentType).Returns(new PublishedContentType(Guid.NewGuid(), 22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing)); return pc; } } diff --git a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs index a7b6d3d18a..4a0af69999 100644 --- a/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ b/src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs @@ -44,7 +44,7 @@ namespace Umbraco.Tests.PublishedContent { } } - class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache + class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache2, IPublishedMediaCache2 { private readonly Dictionary _content = new Dictionary(); @@ -150,6 +150,11 @@ namespace Umbraco.Tests.PublishedContent throw new NotImplementedException(); } + public override IPublishedContentType GetContentType(Guid key) + { + throw new NotImplementedException(); + } + public override IEnumerable GetByContentType(IPublishedContentType contentType) { throw new NotImplementedException(); @@ -378,7 +383,7 @@ namespace Umbraco.Tests.PublishedContent #endregion } - class PublishedContentStrong1 : PublishedContentModel + internal class PublishedContentStrong1 : PublishedContentModel { public PublishedContentStrong1(IPublishedContent content) : base(content) @@ -387,7 +392,7 @@ namespace Umbraco.Tests.PublishedContent public int StrongValue => this.Value("strongValue"); } - class PublishedContentStrong1Sub : PublishedContentStrong1 + internal class PublishedContentStrong1Sub : PublishedContentStrong1 { public PublishedContentStrong1Sub(IPublishedContent content) : base(content) @@ -396,7 +401,7 @@ namespace Umbraco.Tests.PublishedContent public int AnotherValue => this.Value("anotherValue"); } - class PublishedContentStrong2 : PublishedContentModel + internal class PublishedContentStrong2 : PublishedContentModel { public PublishedContentStrong2(IPublishedContent content) : base(content) @@ -405,7 +410,7 @@ namespace Umbraco.Tests.PublishedContent public int StrongValue => this.Value("strongValue"); } - class AutoPublishedContentType : PublishedContentType + internal class AutoPublishedContentType : PublishedContentType { private static readonly IPublishedPropertyType Default; @@ -418,20 +423,20 @@ namespace Umbraco.Tests.PublishedContent Default = factory.CreatePropertyType("*", 666); } - public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) - : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) + public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable propertyTypes) + : base(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) { } - public AutoPublishedContentType(int id, string alias, Func> propertyTypes) - : base(id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) + public AutoPublishedContentType(Guid key, int id, string alias, Func> propertyTypes) + : base(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) { } - public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) + public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) + : base(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) { } - public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, Func> propertyTypes) - : base(id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) + public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable compositionAliases, Func> propertyTypes) + : base(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) { } public override IPublishedPropertyType GetPropertyType(string alias) diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs index 2aa01916fb..25d3eda081 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -27,7 +28,7 @@ namespace Umbraco.Tests.Routing Mock.Of(), Mock.Of()), }; - _publishedContentType = new PublishedContentType(0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); + _publishedContentType = new PublishedContentType(Guid.NewGuid(), 0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); } protected override PublishedContentType GetPublishedContentTypeByAlias(string alias) diff --git a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs index 0c1f89f430..18fe268a9a 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -25,7 +26,7 @@ namespace Umbraco.Tests.Routing Mock.Of(), Mock.Of()), }; - _publishedContentType = new PublishedContentType(0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); + _publishedContentType = new PublishedContentType(Guid.NewGuid(), 0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); } protected override PublishedContentType GetPublishedContentTypeByAlias(string alias) diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 2f960d498d..3a1ff36a0a 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -139,7 +139,7 @@ namespace Umbraco.Tests.Routing property.SetSourceValue("en", enMediaUrl, true); property.SetSourceValue("da", daMediaUrl); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), new [] { umbracoFilePropertyType }, ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) {Properties = new[] {property}}; var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Auto, "da"); @@ -150,7 +150,7 @@ namespace Umbraco.Tests.Routing { var umbracoFilePropertyType = CreatePropertyType(propertyEditorAlias, dataTypeConfiguration, ContentVariation.Nothing); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), new[] {umbracoFilePropertyType}, ContentVariation.Nothing); return new SolidPublishedContent(contentType) diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 135172460d..786eebea9f 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -140,7 +140,7 @@ namespace Umbraco.Tests.Routing frequest.TemplateModel = template; var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var type = new AutoPublishedContentType(22, "CustomDocument", new PublishedPropertyType[] { }); + var type = new AutoPublishedContentType(Guid.NewGuid(), 22, "CustomDocument", new PublishedPropertyType[] { }); ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; var handler = new RenderRouteHandler(umbracoContext, new TestControllerFactory(umbracoContextAccessor, Mock.Of(), context => diff --git a/src/Umbraco.Tests/Routing/UrlProviderTests.cs b/src/Umbraco.Tests/Routing/UrlProviderTests.cs index 02aa95cd2e..8043e25661 100644 --- a/src/Umbraco.Tests/Routing/UrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/UrlProviderTests.cs @@ -158,7 +158,7 @@ namespace Umbraco.Tests.Routing var umbracoSettings = Current.Configs.Settings(); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); @@ -203,7 +203,7 @@ namespace Umbraco.Tests.Routing var umbracoSettings = Current.Configs.Settings(); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); @@ -257,7 +257,7 @@ namespace Umbraco.Tests.Routing var umbracoSettings = Current.Configs.Settings(); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; var publishedContentCache = new Mock(); diff --git a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs index bce9bd4155..6c40e2842d 100644 --- a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs @@ -65,7 +65,7 @@ namespace Umbraco.Tests.Templates { //setup a mock url provider which we'll use for testing - var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var mediaType = new PublishedContentType(Guid.NewGuid(), 777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var media = new Mock(); media.Setup(x => x.ContentType).Returns(mediaType); var mediaUrlProvider = new Mock(); diff --git a/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs b/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs index 7cd96a32ed..861a7e4db6 100644 --- a/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlLocalLinkParserTests.cs @@ -54,12 +54,12 @@ namespace Umbraco.Tests.Templates contentUrlProvider .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/my-test-url")); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = new Mock(); publishedContent.Setup(x => x.Id).Returns(1234); publishedContent.Setup(x => x.ContentType).Returns(contentType); - var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var mediaType = new PublishedContentType(Guid.NewGuid(), 777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var media = new Mock(); media.Setup(x => x.ContentType).Returns(mediaType); var mediaUrlProvider = new Mock(); diff --git a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs index f7e3744600..7d8cedc9c6 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseWebTest.cs @@ -42,7 +42,7 @@ namespace Umbraco.Tests.TestHelpers new DataType(new VoidEditor(Mock.Of())) { Id = 1 }); var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - var type = new AutoPublishedContentType(0, "anything", new PublishedPropertyType[] { }); + var type = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", new PublishedPropertyType[] { }); ContentTypesCache.GetPublishedContentTypeByAlias = alias => GetPublishedContentTypeByAlias(alias) ?? type; } diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index ee91427a63..f53b0bfff0 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -86,7 +86,7 @@ namespace Umbraco.Tests.Testing.TestingTests var theUrlProvider = new UrlProvider(umbracoContext, new [] { urlProvider }, Enumerable.Empty(), umbracoContext.VariationContextAccessor); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = Mock.Of(); Mock.Get(publishedContent).Setup(x => x.ContentType).Returns(contentType); diff --git a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs index 71e74e05fe..7b0f98903c 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListConfiguration.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using System; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors @@ -39,7 +40,7 @@ namespace Umbraco.Web.PropertyEditors public string Thumbnail { get; set; } [JsonProperty("contentTypeKey")] - public string Key { get; set; } + public Guid Key { get; set; } [JsonProperty("settingsElementTypeKey")] public string SettingsElementTypeKey { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs index 3ca70cbd22..917462e2f2 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockEditorConverter.cs @@ -23,13 +23,17 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters JObject sourceObject, string contentTypeKeyPropertyKey, PropertyCacheLevel referenceCacheLevel, bool preview) { - var elementTypeKey = sourceObject[contentTypeKeyPropertyKey]?.ToObject(); - if (string.IsNullOrEmpty(elementTypeKey)) + var elementTypeKey = sourceObject[contentTypeKeyPropertyKey]?.ToObject(); + if (!elementTypeKey.HasValue) return null; + // hack! we need to cast, we have n ochoice beacuse we cannot make breaking changes. + var publishedContentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content as IPublishedContentCache2; + if (publishedContentCache == null) + throw new InvalidOperationException("The published content cache is not " + typeof(IPublishedContentCache2)); + // only convert element types - content types will cause an exception when PublishedModelFactory creates the model - // TODO: make this work with keys. - var publishedContentType = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetContentType(elementTypeKey); + var publishedContentType = publishedContentCache.GetContentType(elementTypeKey.Value); if (publishedContentType == null || publishedContentType.IsElement == false) return null; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs index 7bd410f55a..a833efa2e5 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/BlockListPropertyValueConverter.cs @@ -17,13 +17,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public class BlockListPropertyValueConverter : PropertyValueConverterBase { private readonly IProfilingLogger _proflog; - private readonly IPublishedModelFactory _publishedModelFactory; private readonly BlockEditorConverter _blockConverter; - public BlockListPropertyValueConverter(IProfilingLogger proflog, IPublishedModelFactory publishedModelFactory, BlockEditorConverter blockConverter) + public BlockListPropertyValueConverter(IProfilingLogger proflog, BlockEditorConverter blockConverter) { _proflog = proflog; - _publishedModelFactory = publishedModelFactory; _blockConverter = blockConverter; } @@ -54,11 +52,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var configuration = propertyType.DataType.ConfigurationAs(); var contentTypes = configuration.Blocks; var contentTypeMap = contentTypes.ToDictionary(x => x.Key); - var elements = (contentTypes.Length == 1 - // TODO: make this work with key - ? (IList)_publishedModelFactory.CreateModelList(contentTypes[0].Key) - : new List()) - .ToDictionary(x => x.Key, x => x); + + var elements = new Dictionary(); var layout = new List(); var model = new BlockListModel(elements.Values, layout); @@ -102,8 +97,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (!elements.TryGetValue(guidUdi.Guid, out var data)) continue; - // Make this work with Key, not alias, since contentTypeMap is now a dctionary with contentTypeKey as the dictionary key. - if (!contentTypeMap.TryGetValue(data.ContentType.Key, out var blockConfig)) + if (!data.ContentType.TryGetKey(out var contentTypeKey)) + throw new InvalidOperationException("The content type was not of type " + typeof(IPublishedContentType2)); + + if (!contentTypeMap.TryGetValue(contentTypeKey, out var blockConfig)) continue; // this can happen if they have a settings type, save content, remove the settings type, and display the front-end page before saving the content again diff --git a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs index 0370088f77..4760082908 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedCache.cs @@ -7,6 +7,16 @@ using Umbraco.Core.Xml; namespace Umbraco.Web.PublishedCache { + public interface IPublishedCache2 : IPublishedCache + { + /// + /// Gets a content type identified by its alias. + /// + /// The content type key. + /// The content type, or null. + IPublishedContentType GetContentType(Guid key); + } + /// /// Provides access to cached contents. /// diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs index b4a6e3d1e0..8175285c3a 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs @@ -4,6 +4,11 @@ using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.PublishedCache { + public interface IPublishedContentCache2 : IPublishedContentCache, IPublishedCache2 + { + // NOTE: this is here purely to avoid API breaking changes + } + public interface IPublishedContentCache : IPublishedCache { /// diff --git a/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs index 0b461882b7..702b4fe49d 100644 --- a/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/IPublishedMediaCache.cs @@ -1,5 +1,10 @@ namespace Umbraco.Web.PublishedCache { + public interface IPublishedMediaCache2 : IPublishedMediaCache, IPublishedCache2 + { + // NOTE: this is here purely to avoid API breaking changes + } + public interface IPublishedMediaCache : IPublishedCache { } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs index 24c6a7018b..8e6e517aea 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentCache.cs @@ -13,7 +13,7 @@ using Umbraco.Web.PublishedCache.NuCache.Navigable; namespace Umbraco.Web.PublishedCache.NuCache { - internal class ContentCache : PublishedCacheBase, IPublishedContentCache, INavigableData, IDisposable + internal class ContentCache : PublishedCacheBase, IPublishedContentCache2, INavigableData, IDisposable { private readonly ContentStore.Snapshot _snapshot; private readonly IAppCache _snapshotCache; @@ -384,15 +384,11 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public override IPublishedContentType GetContentType(int id) - { - return _snapshot.GetContentType(id); - } + public override IPublishedContentType GetContentType(int id) => _snapshot.GetContentType(id); - public override IPublishedContentType GetContentType(string alias) - { - return _snapshot.GetContentType(alias); - } + public override IPublishedContentType GetContentType(string alias) => _snapshot.GetContentType(alias); + + public override IPublishedContentType GetContentType(Guid key) => _snapshot.GetContentType(key); #endregion diff --git a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs index a3f918c92c..b39b38ca32 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/ContentStore.cs @@ -37,9 +37,14 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IVariationContextAccessor _variationContextAccessor; private readonly ConcurrentDictionary> _contentNodes; private LinkedNode _root; - private readonly ConcurrentDictionary> _contentTypesById; + + // We must keep separate dictionaries for by id and by alias because we track these in snapshot/layers + // and it is possible that the alias of a content type can be different for the same id in another layer + // whereas the GUID -> INT cross reference can never be different + private readonly ConcurrentDictionary> _contentTypesById; private readonly ConcurrentDictionary> _contentTypesByAlias; - private readonly ConcurrentDictionary _xmap; + private readonly ConcurrentDictionary _contentTypeKeyToIdMap; + private readonly ConcurrentDictionary _contentKeyToIdMap; private readonly ILogger _logger; private BPlusTree _localDb; @@ -73,7 +78,8 @@ namespace Umbraco.Web.PublishedCache.NuCache _root = new LinkedNode(new ContentNode(), 0); _contentTypesById = new ConcurrentDictionary>(); _contentTypesByAlias = new ConcurrentDictionary>(StringComparer.InvariantCultureIgnoreCase); - _xmap = new ConcurrentDictionary(); + _contentTypeKeyToIdMap = new ConcurrentDictionary(); + _contentKeyToIdMap = new ConcurrentDictionary(); _genObjs = new ConcurrentQueue(); _genObj = null; // no initial gen exists @@ -136,7 +142,7 @@ namespace Umbraco.Web.PublishedCache.NuCache Monitor.Enter(_wlocko, ref lockInfo.Taken); - lock(_rlocko) + lock (_rlocko) { // see SnapDictionary try { } @@ -152,7 +158,7 @@ namespace Umbraco.Web.PublishedCache.NuCache _nextGen = true; } } - } + } } private void Release(WriteLockInfo lockInfo, bool commit = true) @@ -291,8 +297,7 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var type in types) { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); + SetContentTypeLocked(type); } } @@ -318,8 +323,7 @@ namespace Umbraco.Web.PublishedCache.NuCache foreach (var type in index.Values) { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); + SetContentTypeLocked(type); } foreach (var link in _contentNodes.Values) @@ -354,8 +358,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // set all new content types foreach (var type in types) { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); + SetContentTypeLocked(type); } // beware! at that point the cache is inconsistent, @@ -419,8 +422,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // perform update of refreshed content types foreach (var type in refreshedTypesA) { - SetValueLocked(_contentTypesById, type.Id, type); - SetValueLocked(_contentTypesByAlias, type.Alias, type); + SetContentTypeLocked(type); } // perform update of content with refreshed content type - from the kits @@ -638,7 +640,7 @@ namespace Umbraco.Web.PublishedCache.NuCache kit.Node.PreviousSiblingContentId = existing.PreviousSiblingContentId; } - _xmap[kit.Node.Uid] = kit.Node.Id; + _contentKeyToIdMap[kit.Node.Uid] = kit.Node.Id; return true; } @@ -734,7 +736,7 @@ namespace Umbraco.Web.PublishedCache.NuCache // this node becomes the previous node previousNode = thisNode; - _xmap[kit.Node.Uid] = kit.Node.Id; + _contentKeyToIdMap[kit.Node.Uid] = kit.Node.Id; } return ok; @@ -757,7 +759,7 @@ namespace Umbraco.Web.PublishedCache.NuCache EnsureLocked(); var ok = true; - + ClearLocked(_contentNodes); ClearRootLocked(); @@ -778,7 +780,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_localDb != null) RegisterChange(kit.Node.Id, kit); AddTreeNodeLocked(kit.Node, parent); - _xmap[kit.Node.Uid] = kit.Node.Id; + _contentKeyToIdMap[kit.Node.Uid] = kit.Node.Id; } return ok; @@ -807,7 +809,7 @@ namespace Umbraco.Web.PublishedCache.NuCache EnsureLocked(); var ok = true; - + // get existing _contentNodes.TryGetValue(rootContentId, out var link); var existing = link?.Value; @@ -833,7 +835,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_localDb != null) RegisterChange(kit.Node.Id, kit); AddTreeNodeLocked(kit.Node, parent); - _xmap[kit.Node.Uid] = kit.Node.Id; + _contentKeyToIdMap[kit.Node.Uid] = kit.Node.Id; } return ok; @@ -885,11 +887,11 @@ namespace Umbraco.Web.PublishedCache.NuCache // This should never be null, all code that calls this method is null checking but we've seen // issues of null ref exceptions in issue reports so we'll double check here if (content == null) throw new ArgumentNullException(nameof(content)); - + SetValueLocked(_contentNodes, content.Id, null); if (_localDb != null) RegisterChange(content.Id, ContentNodeKit.Null); - _xmap.TryRemove(content.Uid, out _); + _contentKeyToIdMap.TryRemove(content.Uid, out _); var id = content.FirstChildContentId; while (id > 0) @@ -913,10 +915,10 @@ namespace Umbraco.Web.PublishedCache.NuCache { if (_contentNodes.TryGetValue(id, out var link)) { - link = GetLinkedNodeGen(link, gen); + link = GetLinkedNodeGen(link, gen); if (link != null && link.Value != null) return link; - } + } throw new PanicException($"failed to get {description} with id={id}"); } @@ -929,13 +931,13 @@ namespace Umbraco.Web.PublishedCache.NuCache { if (content.ParentContentId < 0) { - var root = GetLinkedNodeGen(_root, gen); + var root = GetLinkedNodeGen(_root, gen); return root; } if (_contentNodes.TryGetValue(content.ParentContentId, out var link)) link = GetLinkedNodeGen(link, gen); - + return link; } @@ -1154,6 +1156,15 @@ namespace Umbraco.Web.PublishedCache.NuCache } } + private void SetContentTypeLocked(IPublishedContentType type) + { + SetValueLocked(_contentTypesById, type.Id, type); + SetValueLocked(_contentTypesByAlias, type.Alias, type); + // ensure the key/id map is accurate + if (type.TryGetKey(out var key)) + _contentTypeKeyToIdMap[key] = type.Id; + } + // set a node (just the node, not the tree) private void SetValueLocked(ConcurrentDictionary> dict, TKey key, TValue value) where TValue : class @@ -1211,14 +1222,14 @@ namespace Umbraco.Web.PublishedCache.NuCache public ContentNode Get(Guid uid, long gen) { - return _xmap.TryGetValue(uid, out var id) + return _contentKeyToIdMap.TryGetValue(uid, out var id) ? GetValue(_contentNodes, id, gen) : null; } public IEnumerable GetAtRoot(long gen) { - var root = GetLinkedNodeGen(_root, gen); + var root = GetLinkedNodeGen(_root, gen); if (root == null) yield break; @@ -1274,13 +1285,20 @@ namespace Umbraco.Web.PublishedCache.NuCache return GetValue(_contentTypesByAlias, alias, gen); } + public IPublishedContentType GetContentType(Guid key, long gen) + { + if (!_contentTypeKeyToIdMap.TryGetValue(key, out var id)) + return null; + return GetContentType(id, gen); + } + #endregion #region Snapshots public Snapshot CreateSnapshot() { - lock(_rlocko) + lock (_rlocko) { // if no next generation is required, and we already have one, // use it and create a new snapshot @@ -1606,6 +1624,13 @@ namespace Umbraco.Web.PublishedCache.NuCache return _store.GetContentType(alias, _gen); } + public IPublishedContentType GetContentType(Guid key) + { + if (_gen < 0) + throw new ObjectDisposedException("snapshot" /*+ " (" + _thisCount + ")"*/); + return _store.GetContentType(key, _gen); + } + // this code is here just so you don't try to implement it // the only way we can iterate over "all" without locking the entire cache forever // is by shallow cloning the cache, which is quite expensive, so we should probably not do it, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs index 182086ed7f..a466460ede 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MediaCache.cs @@ -11,7 +11,7 @@ using Umbraco.Web.PublishedCache.NuCache.Navigable; namespace Umbraco.Web.PublishedCache.NuCache { - internal class MediaCache : PublishedCacheBase, IPublishedMediaCache, INavigableData, IDisposable + internal class MediaCache : PublishedCacheBase, IPublishedMediaCache2, INavigableData, IDisposable { private readonly ContentStore.Snapshot _snapshot; private readonly IVariationContextAccessor _variationContextAccessor; @@ -155,15 +155,11 @@ namespace Umbraco.Web.PublishedCache.NuCache #region Content types - public override IPublishedContentType GetContentType(int id) - { - return _snapshot.GetContentType(id); - } + public override IPublishedContentType GetContentType(int id) => _snapshot.GetContentType(id); - public override IPublishedContentType GetContentType(string alias) - { - return _snapshot.GetContentType(alias); - } + public override IPublishedContentType GetContentType(string alias) => _snapshot.GetContentType(alias); + + public override IPublishedContentType GetContentType(Guid key) => _snapshot.GetContentType(key); #endregion diff --git a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs index 1f637663e5..1b4a9bb92a 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedCacheBase.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Xml; namespace Umbraco.Web.PublishedCache { - abstract class PublishedCacheBase : IPublishedCache + internal abstract class PublishedCacheBase : IPublishedCache2 { public bool PreviewDefault { get; } @@ -89,8 +89,8 @@ namespace Umbraco.Web.PublishedCache } public abstract IPublishedContentType GetContentType(int id); - public abstract IPublishedContentType GetContentType(string alias); + public abstract IPublishedContentType GetContentType(Guid key); public virtual IEnumerable GetByContentType(IPublishedContentType contentType) { diff --git a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs b/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs index e453471bb8..8eb50b0588 100644 --- a/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs +++ b/src/Umbraco.Web/PublishedCache/PublishedContentTypeCache.cs @@ -15,8 +15,10 @@ namespace Umbraco.Web.PublishedCache /// This cache is not snapshotted, so it refreshes any time things change. public class PublishedContentTypeCache { + // NOTE: These are not concurrent dictionaries because all access is done within a lock private readonly Dictionary _typesByAlias = new Dictionary(); private readonly Dictionary _typesById = new Dictionary(); + private readonly Dictionary _keyToIdMap = new Dictionary(); private readonly IContentTypeService _contentTypeService; private readonly IMediaTypeService _mediaTypeService; private readonly IMemberTypeService _memberTypeService; @@ -130,6 +132,42 @@ namespace Umbraco.Web.PublishedCache } } + /// + /// Gets a published content type. + /// + /// An item type. + /// An key. + /// The published content type corresponding to the item key. + public IPublishedContentType Get(PublishedItemType itemType, Guid key) + { + try + { + _lock.EnterUpgradeableReadLock(); + + if (_keyToIdMap.TryGetValue(key, out var id)) + return Get(itemType, id); + + var type = CreatePublishedContentType(itemType, key); + + try + { + _lock.EnterWriteLock(); + _keyToIdMap[key] = type.Id; + return _typesByAlias[GetAliasKey(type)] = _typesById[type.Id] = type; + } + finally + { + if (_lock.IsWriteLockHeld) + _lock.ExitWriteLock(); + } + } + finally + { + if (_lock.IsUpgradeableReadLockHeld) + _lock.ExitUpgradeableReadLock(); + } + } + /// /// Gets a published content type. /// @@ -152,7 +190,8 @@ namespace Umbraco.Web.PublishedCache try { _lock.EnterWriteLock(); - + if (type.TryGetKey(out var key)) + _keyToIdMap[key] = type.Id; return _typesByAlias[aliasKey] = _typesById[type.Id] = type; } finally @@ -188,7 +227,8 @@ namespace Umbraco.Web.PublishedCache try { _lock.EnterWriteLock(); - + if (type.TryGetKey(out var key)) + _keyToIdMap[key] = type.Id; return _typesByAlias[GetAliasKey(type)] = _typesById[type.Id] = type; } finally @@ -204,27 +244,32 @@ namespace Umbraco.Web.PublishedCache } } + private IPublishedContentType CreatePublishedContentType(PublishedItemType itemType, Guid key) + { + IContentTypeComposition contentType = itemType switch + { + PublishedItemType.Content => _contentTypeService.Get(key), + PublishedItemType.Media => _mediaTypeService.Get(key), + PublishedItemType.Member => _memberTypeService.Get(key), + _ => throw new ArgumentOutOfRangeException(nameof(itemType)), + }; + if (contentType == null) + throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with key \"{key}\"."); + + return _publishedContentTypeFactory.CreateContentType(contentType); + } + private IPublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias) { if (GetPublishedContentTypeByAlias != null) return GetPublishedContentTypeByAlias(alias); - - IContentTypeComposition contentType; - switch (itemType) + IContentTypeComposition contentType = itemType switch { - case PublishedItemType.Content: - contentType = _contentTypeService.Get(alias); - break; - case PublishedItemType.Media: - contentType = _mediaTypeService.Get(alias); - break; - case PublishedItemType.Member: - contentType = _memberTypeService.Get(alias); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType)); - } - + PublishedItemType.Content => _contentTypeService.Get(alias), + PublishedItemType.Media => _mediaTypeService.Get(alias), + PublishedItemType.Member => _memberTypeService.Get(alias), + _ => throw new ArgumentOutOfRangeException(nameof(itemType)), + }; if (contentType == null) throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with alias \"{alias}\"."); @@ -235,23 +280,13 @@ namespace Umbraco.Web.PublishedCache { if (GetPublishedContentTypeById != null) return GetPublishedContentTypeById(id); - - IContentTypeComposition contentType; - switch (itemType) + IContentTypeComposition contentType = itemType switch { - case PublishedItemType.Content: - contentType = _contentTypeService.Get(id); - break; - case PublishedItemType.Media: - contentType = _mediaTypeService.Get(id); - break; - case PublishedItemType.Member: - contentType = _memberTypeService.Get(id); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType)); - } - + PublishedItemType.Content => _contentTypeService.Get(id), + PublishedItemType.Media => _mediaTypeService.Get(id), + PublishedItemType.Member => _memberTypeService.Get(id), + _ => throw new ArgumentOutOfRangeException(nameof(itemType)), + }; if (contentType == null) throw new Exception($"ContentTypeService failed to find a {itemType.ToString().ToLower()} type with id {id}."); @@ -259,6 +294,7 @@ namespace Umbraco.Web.PublishedCache } // for unit tests - changing the callback must reset the cache obviously + // TODO: Why does this even exist? For testing you'd pass in a mocked service to get by id private Func _getPublishedContentTypeByAlias; internal Func GetPublishedContentTypeByAlias { @@ -282,6 +318,7 @@ namespace Umbraco.Web.PublishedCache } // for unit tests - changing the callback must reset the cache obviously + // TODO: Why does this even exist? For testing you'd pass in a mocked service to get by id private Func _getPublishedContentTypeById; internal Func GetPublishedContentTypeById { diff --git a/src/umbraco.sln b/src/umbraco.sln index 63fb856b5d..78ff0ef12d 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -58,8 +58,24 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.Client", "ht StartServerOnDebug = "false" EndProjectSection EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Tests.AcceptanceTest\", "Umbraco.Tests.AcceptanceTest\", "{9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}" +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Tests.AcceptanceTest", "Umbraco.Tests.AcceptanceTest\", "{9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}" ProjectSection(WebsiteProperties) = preProject + TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" + Debug.AspNetCompiler.VirtualPath = "/localhost_62926" + Debug.AspNetCompiler.PhysicalPath = "Umbraco.Tests.AcceptanceTest\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_62926\" + Debug.AspNetCompiler.Updateable = "true" + Debug.AspNetCompiler.ForceOverwrite = "true" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.VirtualPath = "/localhost_62926" + Release.AspNetCompiler.PhysicalPath = "Umbraco.Tests.AcceptanceTest\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_62926\" + Release.AspNetCompiler.Updateable = "true" + Release.AspNetCompiler.ForceOverwrite = "true" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.Debug = "False" + VWDPort = "62926" SlnRelativePath = "Umbraco.Tests.AcceptanceTest\" EndProjectSection EndProject @@ -123,6 +139,9 @@ Global {4C4C194C-B5E4-4991-8F87-4373E24CC19F}.Release|Any CPU.Build.0 = Release|Any CPU {3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.Release|Any CPU.Build.0 = Debug|Any CPU {651E1350-91B6-44B7-BD60-7207006D7003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {651E1350-91B6-44B7-BD60-7207006D7003}.Debug|Any CPU.Build.0 = Debug|Any CPU {651E1350-91B6-44B7-BD60-7207006D7003}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -157,6 +176,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {227C3B55-80E5-4E7E-A802-BE16C5128B9D} = {2849E9D4-3B4E-40A3-A309-F3CB4F0E125F} + {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {5D3B8245-ADA6-453F-A008-50ED04BFE770} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {E3F9F378-AFE1-40A5-90BD-82833375DBFE} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} {5B03EF4E-E0AC-4905-861B-8C3EC1A0D458} = {227C3B55-80E5-4E7E-A802-BE16C5128B9D} @@ -164,7 +184,6 @@ Global {3A33ADC9-C6C0-4DB1-A613-A9AF0210DF3D} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} {C7311C00-2184-409B-B506-52A5FAEA8736} = {FD962632-184C-4005-A5F3-E705D92FC645} {FB5676ED-7A69-492C-B802-E7B24144C0FC} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} - {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817} = {B5BD12C1-A454-435E-8A46-FF4A364C0382} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A0F2E34-D2AF-4DAB-86A0-7D7764B3D0EC}