diff --git a/src/Umbraco.Core/Models/IContentBase.cs b/src/Umbraco.Core/Models/IContentBase.cs index 0f660181fb..f4fae44ffc 100644 --- a/src/Umbraco.Core/Models/IContentBase.cs +++ b/src/Umbraco.Core/Models/IContentBase.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Models.Entities; namespace Umbraco.Core.Models { + /// /// Provides a base class for content items. /// diff --git a/src/Umbraco.Core/Models/IReadOnlyContentBase.cs b/src/Umbraco.Core/Models/IReadOnlyContentBase.cs new file mode 100644 index 0000000000..3ed39b63ab --- /dev/null +++ b/src/Umbraco.Core/Models/IReadOnlyContentBase.cs @@ -0,0 +1,72 @@ +using System; + +namespace Umbraco.Core.Models +{ + public interface IReadOnlyContentBase + { + /// + /// Gets the integer identifier of the entity. + /// + int Id { get; } + + /// + /// Gets the Guid unique identifier of the entity. + /// + Guid Key { get; } + + /// + /// Gets the creation date. + /// + DateTime CreateDate { get; } + + /// + /// Gets the last update date. + /// + DateTime UpdateDate { get; } + + /// + /// Gets the name of the entity. + /// + string Name { get; } + + /// + /// Gets the identifier of the user who created this entity. + /// + int CreatorId { get; } + + /// + /// Gets the identifier of the parent entity. + /// + int ParentId { get; } + + /// + /// Gets the level of the entity. + /// + int Level { get; } + + /// + /// Gets the path to the entity. + /// + string Path { get; } + + /// + /// Gets the sort order of the entity. + /// + int SortOrder { get; } + + /// + /// Gets the content type id + /// + int ContentTypeId { get; } + + /// + /// Gets the identifier of the writer. + /// + int WriterId { get; } + + /// + /// Gets the version identifier. + /// + int VersionId { get; } + } +} diff --git a/src/Umbraco.Core/Models/ReadOnlyContentBaseAdapter.cs b/src/Umbraco.Core/Models/ReadOnlyContentBaseAdapter.cs new file mode 100644 index 0000000000..f707d2ab1c --- /dev/null +++ b/src/Umbraco.Core/Models/ReadOnlyContentBaseAdapter.cs @@ -0,0 +1,42 @@ +using System; + +namespace Umbraco.Core.Models +{ + internal struct ReadOnlyContentBaseAdapter : IReadOnlyContentBase + { + private readonly IContentBase _content; + + private ReadOnlyContentBaseAdapter(IContentBase content) + { + _content = content ?? throw new ArgumentNullException(nameof(content)); + } + + public static ReadOnlyContentBaseAdapter Create(IContentBase content) => new ReadOnlyContentBaseAdapter(content); + + public int Id => _content.Id; + + public Guid Key => _content.Key; + + public DateTime CreateDate => _content.CreateDate; + + public DateTime UpdateDate => _content.UpdateDate; + + public string Name => _content.Name; + + public int CreatorId => _content.CreatorId; + + public int ParentId => _content.ParentId; + + public int Level => _content.Level; + + public string Path => _content.Path; + + public int SortOrder => _content.SortOrder; + + public int ContentTypeId => _content.ContentTypeId; + + public int WriterId => _content.WriterId; + + public int VersionId => _content.VersionId; + } +} diff --git a/src/Umbraco.Core/PropertyEditors/CompressedStorageAttribute.cs b/src/Umbraco.Core/PropertyEditors/CompressedStorageAttribute.cs deleted file mode 100644 index 31689c4ee9..0000000000 --- a/src/Umbraco.Core/PropertyEditors/CompressedStorageAttribute.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace Umbraco.Core.PropertyEditors -{ - /// - /// When assigned to a DataEditor it indicates that the values it generates can be compressed - /// - /// - /// Used in conjunction with - /// - [AttributeUsage(AttributeTargets.Class)] - public sealed class CompressedStorageAttribute : Attribute - { - public CompressedStorageAttribute(bool isCompressed = true) - { - IsCompressed = isCompressed; - } - - public bool IsCompressed { get; } - } -} diff --git a/src/Umbraco.Core/PropertyEditors/CompressedStoragePropertyEditorCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/CompressedStoragePropertyEditorCompressionOptions.cs deleted file mode 100644 index a99452a5b1..0000000000 --- a/src/Umbraco.Core/PropertyEditors/CompressedStoragePropertyEditorCompressionOptions.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Models; - -namespace Umbraco.Core.PropertyEditors -{ - - /// - /// Ensures all property types that have a property editor attributed with use data compression - /// - internal class CompressedStoragePropertyEditorCompressionOptions : IPropertyCompressionOptions - { - private readonly IReadOnlyDictionary _contentTypes; - private readonly PropertyEditorCollection _propertyEditors; - private readonly ConcurrentDictionary<(int, string), CompressedStorageAttribute> _compressedStoragePropertyEditorCache; - - public CompressedStoragePropertyEditorCompressionOptions( - IReadOnlyDictionary contentTypes, - PropertyEditorCollection propertyEditors, - ConcurrentDictionary<(int, string), CompressedStorageAttribute> compressedStoragePropertyEditorCache) - { - _contentTypes = contentTypes ?? throw new System.ArgumentNullException(nameof(contentTypes)); - _propertyEditors = propertyEditors ?? throw new System.ArgumentNullException(nameof(propertyEditors)); - _compressedStoragePropertyEditorCache = compressedStoragePropertyEditorCache; - } - - public bool IsCompressed(int contentTypeId, string alias) - { - var compressedStorage = _compressedStoragePropertyEditorCache.GetOrAdd((contentTypeId, alias), x => - { - if (!_contentTypes.TryGetValue(contentTypeId, out var ct)) - return null; - - var propertyType = ct.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == alias); - if (propertyType == null) return null; - - if (!_propertyEditors.TryGet(propertyType.PropertyEditorAlias, out var propertyEditor)) return null; - - var attribute = propertyEditor.GetType().GetCustomAttribute(true); - return attribute; - }); - - return compressedStorage?.IsCompressed ?? false; - } - } -} diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs similarity index 50% rename from src/Umbraco.Core/PropertyEditors/IPropertyCompressionOptions.cs rename to src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs index d1add38f19..96a559630b 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs @@ -3,10 +3,13 @@ namespace Umbraco.Core.PropertyEditors { /// - /// Determines if a property type's value should be compressed + /// Determines if a property type's value should be compressed in memory /// - public interface IPropertyCompressionOptions + /// + /// + /// + public interface IPropertyCacheCompression { - bool IsCompressed(int contentTypeId, string propertyTypeAlias); + bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias); } } diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs new file mode 100644 index 0000000000..2fa0153f9e --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Core.PropertyEditors +{ + public interface IPropertyCacheCompressionOptions + { + bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor); + } +} diff --git a/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs new file mode 100644 index 0000000000..1f12d45769 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs @@ -0,0 +1,12 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Default implementation for which does not compress any property data + /// + internal class NoopPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions + { + public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor) => false; + } +} diff --git a/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs b/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs new file mode 100644 index 0000000000..6be21fca7f --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs @@ -0,0 +1,49 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; + +namespace Umbraco.Core.PropertyEditors +{ + + /// + /// Compresses property data based on config + /// + internal class PropertyCacheCompression : IPropertyCacheCompression + { + private readonly IPropertyCacheCompressionOptions _compressionOptions; + private readonly IReadOnlyDictionary _contentTypes; + private readonly PropertyEditorCollection _propertyEditors; + private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias), bool> _isCompressedCache; + + public PropertyCacheCompression( + IPropertyCacheCompressionOptions compressionOptions, + IReadOnlyDictionary contentTypes, + PropertyEditorCollection propertyEditors, + ConcurrentDictionary<(int, string), bool> compressedStoragePropertyEditorCache) + { + _compressionOptions = compressionOptions; + _contentTypes = contentTypes ?? throw new System.ArgumentNullException(nameof(contentTypes)); + _propertyEditors = propertyEditors ?? throw new System.ArgumentNullException(nameof(propertyEditors)); + _isCompressedCache = compressedStoragePropertyEditorCache; + } + + public bool IsCompressed(IReadOnlyContentBase content, string alias) + { + var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias), x => + { + if (!_contentTypes.TryGetValue(x.contentTypeId, out var ct)) + return false; + + var propertyType = ct.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == alias); + if (propertyType == null) return false; + + if (!_propertyEditors.TryGet(propertyType.PropertyEditorAlias, out var propertyEditor)) return false; + + return _compressionOptions.IsCompressed(content, propertyType, propertyEditor); + }); + + return compressedStorage; + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 6c473235a7..a146d5cc4a 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -152,14 +152,17 @@ + + - - - + + + + diff --git a/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs b/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs index 4be80083b8..b3543dad1a 100644 --- a/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs +++ b/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs @@ -2,6 +2,7 @@ using NUnit.Framework; using System; using System.Collections.Generic; +using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Web.PublishedCache.NuCache.DataSource; @@ -11,13 +12,13 @@ namespace Umbraco.Tests.PublishedContent public class ContentSerializationTests { [Test] - public void Ensure_Same_Results() + public void GivenACacheModel_WhenItsSerializedAndDeserializedWithAnySerializer_TheResultsAreTheSame() { var jsonSerializer = new JsonContentNestedDataSerializer(); - var msgPackSerializer = new MsgPackContentNestedDataSerializer(Mock.Of()); + var msgPackSerializer = new MsgPackContentNestedDataSerializer(Mock.Of()); var now = DateTime.Now; - var content = new ContentCacheDataModel + var cacheModel = new ContentCacheDataModel { PropertyData = new Dictionary { @@ -53,14 +54,16 @@ namespace Umbraco.Tests.PublishedContent UrlSegment = "home" }; - var json = jsonSerializer.Serialize(1, content).StringData; - var msgPack = msgPackSerializer.Serialize(1, content).ByteData; + var content = Mock.Of(x => x.ContentTypeId == 1); + + var json = jsonSerializer.Serialize(content, cacheModel).StringData; + var msgPack = msgPackSerializer.Serialize(content, cacheModel).ByteData; Console.WriteLine(json); Console.WriteLine(msgPackSerializer.ToJson(msgPack)); - var jsonContent = jsonSerializer.Deserialize(1, json, null); - var msgPackContent = msgPackSerializer.Deserialize(1, null, msgPack); + var jsonContent = jsonSerializer.Deserialize(content, json, null); + var msgPackContent = msgPackSerializer.Deserialize(content, null, msgPack); CollectionAssert.AreEqual(jsonContent.CultureData.Keys, msgPackContent.CultureData.Keys); diff --git a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs index 2c1221e99e..42023382f1 100644 --- a/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/BlockListPropertyEditor.cs @@ -13,7 +13,6 @@ namespace Umbraco.Web.PropertyEditors /// /// Represents a block list property editor. /// - [CompressedStorage] [DataEditor( Constants.PropertyEditors.Aliases.BlockList, "Block List", diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 7ce312c516..862837381a 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -17,7 +17,6 @@ namespace Umbraco.Web.PropertyEditors /// /// Represents a grid property and parameter editor. /// - [CompressedStorage] [DataEditor( Constants.PropertyEditors.Aliases.Grid, "Grid layout", diff --git a/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs index 6ce66aaa00..2d66da5461 100644 --- a/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MarkdownPropertyEditor.cs @@ -7,7 +7,6 @@ namespace Umbraco.Web.PropertyEditors /// /// Represents a markdown editor. /// - [CompressedStorage] [DataEditor( Constants.PropertyEditors.Aliases.MarkdownEditor, "Markdown editor", diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index 0e1cea3399..ca3c5a2a04 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -22,7 +22,6 @@ namespace Umbraco.Web.PropertyEditors /// /// Represents a nested content property editor. /// - [CompressedStorage] [DataEditor( Constants.PropertyEditors.Aliases.NestedContent, "Nested Content", diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 7c7a358bf3..42777f11ad 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -17,7 +17,6 @@ namespace Umbraco.Web.PropertyEditors /// /// Represents a rich text property editor. /// - [CompressedStorage] [DataEditor( Constants.PropertyEditors.Aliases.TinyMce, "Rich Text Editor", diff --git a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs index 878330820a..c7bc2efbda 100644 --- a/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TextAreaPropertyEditor.cs @@ -7,7 +7,6 @@ namespace Umbraco.Web.PropertyEditors /// /// Represents a textarea property and parameter editor. /// - [CompressedStorage] [DataEditor( Constants.PropertyEditors.Aliases.TextArea, EditorType.PropertyValue | EditorType.MacroParameter, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSourceDto.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSourceDto.cs index be2f9921d1..343885b037 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSourceDto.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentSourceDto.cs @@ -1,12 +1,13 @@ using System; +using Umbraco.Core.Models; namespace Umbraco.Web.PublishedCache.NuCache.DataSource { // read-only dto - internal class ContentSourceDto + internal class ContentSourceDto : IReadOnlyContentBase { public int Id { get; set; } - public Guid Uid { get; set; } + public Guid Key { get; set; } public int ContentTypeId { get; set; } public int Level { get; set; } @@ -37,5 +38,10 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource public int PubTemplateId { get; set; } public string PubData { get; set; } public byte[] PubDataRaw { get; set; } + + // Explicit implementation + DateTime IReadOnlyContentBase.UpdateDate => EditVersionDate; + string IReadOnlyContentBase.Name => EditName; + int IReadOnlyContentBase.WriterId => EditWriterId; } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs index 3bd30d2d73..4584344891 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs @@ -383,7 +383,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } else { - var deserializedContent = serializer.Deserialize(dto.ContentTypeId, dto.EditData, dto.EditDataRaw); + var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw); d = new ContentData { @@ -410,7 +410,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } else { - var deserializedContent = serializer.Deserialize(dto.ContentTypeId, dto.PubData, dto.PubDataRaw); + var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw); p = new ContentData { @@ -427,7 +427,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } } - var n = new ContentNode(dto.Id, dto.Uid, + var n = new ContentNode(dto.Id, dto.Key, dto.Level, dto.Path, dto.SortOrder, dto.ParentId, dto.CreateDate, dto.CreatorId); var s = new ContentNodeKit @@ -446,7 +446,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource if (dto.EditData == null && dto.EditDataRaw == null) throw new InvalidOperationException("No data for media " + dto.Id); - var deserializedMedia = serializer.Deserialize(dto.ContentTypeId, dto.EditData, dto.EditDataRaw); + var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw); var p = new ContentData { @@ -460,7 +460,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource CultureInfos = deserializedMedia.CultureData }; - var n = new ContentNode(dto.Id, dto.Uid, + var n = new ContentNode(dto.Id, dto.Key, dto.Level, dto.Path, dto.SortOrder, dto.ParentId, dto.CreateDate, dto.CreatorId); var s = new ContentNodeKit diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs index f628c8981b..d1a83d8452 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs @@ -1,4 +1,4 @@ -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Models; namespace Umbraco.Web.PublishedCache.NuCache.DataSource { @@ -14,19 +14,12 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// /// Deserialize the data into a /// - /// - /// - /// - /// - ContentCacheDataModel Deserialize(int contentTypeId, string stringData, byte[] byteData); + ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData); /// /// Serializes the /// - /// - /// - /// - ContentCacheDataSerializationResult Serialize(int contentTypeId, ContentCacheDataModel model); + ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs index e7bb5656bb..c4d40f721f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using Umbraco.Core.Models; using Umbraco.Core.Serialization; namespace Umbraco.Web.PublishedCache.NuCache.DataSource @@ -21,7 +22,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource DateFormatString = "o" }; - public ContentCacheDataModel Deserialize(int contentTypeId, string stringData, byte[] byteData) + public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData) { if (stringData == null && byteData != null) throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization"); @@ -29,7 +30,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return JsonConvert.DeserializeObject(stringData, _jsonSerializerSettings); } - public ContentCacheDataSerializationResult Serialize(int contentTypeId, ContentCacheDataModel model) + public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model) { // note that numeric values (which are Int32) are serialized without their // type (eg "value":1234) and JsonConvert by default deserializes them as Int64 diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs index 6ffa130da0..944d93107d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs @@ -4,6 +4,7 @@ using MessagePack.Resolvers; using System; using System.Linq; using System.Text; +using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PublishedCache.NuCache.DataSource @@ -15,9 +16,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource public class MsgPackContentNestedDataSerializer : IContentCacheDataSerializer { private readonly MessagePackSerializerOptions _options; - private readonly IPropertyCompressionOptions _propertyOptions; + private readonly IPropertyCacheCompression _propertyOptions; - public MsgPackContentNestedDataSerializer(IPropertyCompressionOptions propertyOptions) + public MsgPackContentNestedDataSerializer(IPropertyCacheCompression propertyOptions) { _propertyOptions = propertyOptions ?? throw new ArgumentNullException(nameof(propertyOptions)); @@ -46,21 +47,21 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return json; } - public ContentCacheDataModel Deserialize(int contentTypeId, string stringData, byte[] byteData) + public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData) { if (byteData != null) { - var content = MessagePackSerializer.Deserialize(byteData, _options); - Expand(contentTypeId, content); - return content; + var cacheModel = MessagePackSerializer.Deserialize(byteData, _options); + Expand(content, cacheModel); + return cacheModel; } else if (stringData != null) { // NOTE: We don't really support strings but it's possible if manually used (i.e. tests) var bin = Convert.FromBase64String(stringData); - var content = MessagePackSerializer.Deserialize(bin, _options); - Expand(contentTypeId, content); - return content; + var cacheModel = MessagePackSerializer.Deserialize(bin, _options); + Expand(content, cacheModel); + return cacheModel; } else { @@ -68,9 +69,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } } - public ContentCacheDataSerializationResult Serialize(int contentTypeId, ContentCacheDataModel model) + public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model) { - Compress(contentTypeId, model); + Compress(content, model); var bytes = MessagePackSerializer.Serialize(model, _options); return new ContentCacheDataSerializationResult(null, bytes); } @@ -86,11 +87,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// read/decompressed as a string to be displayed on the front-end. This allows for potentially a significant /// memory savings but could also affect performance of first rendering pages while decompression occurs. /// - private void Compress(int contentTypeId, ContentCacheDataModel model) + private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model) { foreach(var propertyAliasToData in model.PropertyData) { - if (_propertyOptions.IsCompressed(contentTypeId, propertyAliasToData.Key)) + if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key)) { foreach(var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string)) { @@ -104,11 +105,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// Used during deserialization to map the property data as lazy or expand the value /// /// - private void Expand(int contentTypeId, ContentCacheDataModel nestedData) + private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData) { foreach (var propertyAliasToData in nestedData.PropertyData) { - if (_propertyOptions.IsCompressed(contentTypeId, propertyAliasToData.Key)) + if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key)) { foreach (var property in propertyAliasToData.Value.Where(x => x.Value != null)) { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs index b509334604..fcc3fa2bb8 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs @@ -12,14 +12,21 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource private readonly IMediaTypeService _mediaTypeService; private readonly IMemberTypeService _memberTypeService; private readonly PropertyEditorCollection _propertyEditors; - private readonly ConcurrentDictionary<(int, string), CompressedStorageAttribute> _compressedStoragePropertyEditorCache = new ConcurrentDictionary<(int, string), CompressedStorageAttribute>(); + private readonly IPropertyCacheCompressionOptions _compressionOptions; + private readonly ConcurrentDictionary<(int, string), bool> _isCompressedCache = new ConcurrentDictionary<(int, string), bool>(); - public MsgPackContentNestedDataSerializerFactory(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, PropertyEditorCollection propertyEditors) + public MsgPackContentNestedDataSerializerFactory( + IContentTypeService contentTypeService, + IMediaTypeService mediaTypeService, + IMemberTypeService memberTypeService, + PropertyEditorCollection propertyEditors, + IPropertyCacheCompressionOptions compressionOptions) { _contentTypeService = contentTypeService; _mediaTypeService = mediaTypeService; _memberTypeService = memberTypeService; _propertyEditors = propertyEditors; + _compressionOptions = compressionOptions; } public IContentCacheDataSerializer Create(ContentCacheDataSerializerEntityType types) @@ -53,8 +60,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } } - var options = new CompressedStoragePropertyEditorCompressionOptions(contentTypes, _propertyEditors, _compressedStoragePropertyEditorCache); - var serializer = new MsgPackContentNestedDataSerializer(options); + var compression = new PropertyCacheCompression(_compressionOptions, contentTypes, _propertyEditors, _isCompressedCache); + var serializer = new MsgPackContentNestedDataSerializer(compression); return serializer; } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs index faeb4f90b4..98d8b91386 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs @@ -20,9 +20,11 @@ namespace Umbraco.Web.PublishedCache.NuCache } else { - composition.RegisterUnique(); + composition.RegisterUnique(); } - + + composition.RegisterUnique(); + composition.RegisterUnique(factory => new ContentDataSerializer(new DictionaryOfPropertyDataSerializer())); // register the NuCache database data source diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index ad8705ef47..dbbcb4ee9b 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -1456,7 +1456,7 @@ namespace Umbraco.Web.PublishedCache.NuCache UrlSegment = content.GetUrlSegment(_urlSegmentProviders) }; - var serialized = serializer.Serialize(content.ContentTypeId, contentCacheData); + var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData); var dto = new ContentNuDto {