diff --git a/Directory.Packages.props b/Directory.Packages.props index 7ac65610e9..de21c2431b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -19,6 +19,7 @@ + diff --git a/src/Umbraco.Core/Cache/DataTypeConfigurationCache.cs b/src/Umbraco.Core/Cache/DataTypeConfigurationCache.cs new file mode 100644 index 0000000000..78ee17d2f8 --- /dev/null +++ b/src/Umbraco.Core/Cache/DataTypeConfigurationCache.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.Caching.Memory; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Services; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.Cache; + +/// +/// This cache is a temporary measure to reduce the amount of computational power required to deserialize and initialize when fetched from the main cache/database, +/// because datatypes are fetched multiple times troughout a (backoffice content) request with a lot of content (or nested content) and each of these fetches initializes certain fields on the datatypes. +/// +internal sealed class DataTypeConfigurationCache : IDataTypeConfigurationCache +{ + private readonly IDataTypeService _dataTypeService; + private readonly IMemoryCache _memoryCache; + + public DataTypeConfigurationCache(IDataTypeService dataTypeService, IMemoryCache memoryCache, IIdKeyMap idKeyMap) + { + _dataTypeService = dataTypeService; + _memoryCache = memoryCache; + } + + public T? GetConfigurationAs(Guid key) + where T : class + { + var cacheKey = GetCacheKey(key); + if (_memoryCache.TryGetValue(cacheKey, out T? configuration) is false) + { + IDataType? dataType = _dataTypeService.GetDataType(key); + configuration = dataType?.ConfigurationAs(); + + // Only cache if data type was found (but still cache null configurations) + if (dataType is not null) + { + _memoryCache.Set(cacheKey, configuration); + } + } + + return configuration; + } + + public void ClearCache(IEnumerable keys) + { + foreach (Guid key in keys) + { + _memoryCache.Remove(GetCacheKey(key)); + } + } + + private static string GetCacheKey(Guid key) => $"DataTypeConfigurationCache_{key}"; +} diff --git a/src/Umbraco.Core/Cache/DataTypeConfigurationCacheRefresher.cs b/src/Umbraco.Core/Cache/DataTypeConfigurationCacheRefresher.cs new file mode 100644 index 0000000000..071acc795f --- /dev/null +++ b/src/Umbraco.Core/Cache/DataTypeConfigurationCacheRefresher.cs @@ -0,0 +1,15 @@ +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Notifications; + +namespace Umbraco.Cms.Core.Cache; + +internal sealed class DataTypeConfigurationCacheRefresher : INotificationHandler +{ + private readonly IDataTypeConfigurationCache _dataTypeConfigurationCache; + + public DataTypeConfigurationCacheRefresher(IDataTypeConfigurationCache dataTypeConfigurationCache) + => _dataTypeConfigurationCache = dataTypeConfigurationCache; + + public void Handle(DataTypeCacheRefresherNotification notification) + => _dataTypeConfigurationCache.ClearCache(((DataTypeCacheRefresher.JsonPayload[])notification.MessageObject).Select(x => x.Key)); +} diff --git a/src/Umbraco.Core/Cache/IDataTypeConfigurationCache.cs b/src/Umbraco.Core/Cache/IDataTypeConfigurationCache.cs new file mode 100644 index 0000000000..7bb7821fc4 --- /dev/null +++ b/src/Umbraco.Core/Cache/IDataTypeConfigurationCache.cs @@ -0,0 +1,33 @@ +namespace Umbraco.Cms.Core.Cache; + +/// +/// Represents a cache for configuration. +/// +public interface IDataTypeConfigurationCache +{ + /// + /// Gets the data type configuration. + /// + /// The data type key. + /// + /// The data type configuration. + /// + object? GetConfiguration(Guid key) => GetConfigurationAs(key); + + /// + /// Gets the data type configuration as . + /// + /// The data type configuration type. + /// The data type key. + /// + /// The data type configuration as . + /// + T? GetConfigurationAs(Guid key) + where T : class; + + /// + /// Clears the cache for the specified keys. + /// + /// The keys. + void ClearCache(IEnumerable keys); +} diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 9d2b52bc76..db290fb146 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -371,6 +371,10 @@ namespace Umbraco.Cms.Core.DependencyInjection Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); + + // Data type configuration cache + Services.AddUnique(); + Services.AddNotificationHandler(); //Two factor providers Services.AddUnique(); diff --git a/src/Umbraco.Core/Models/Mapping/ContentPropertyDisplayMapper.cs b/src/Umbraco.Core/Models/Mapping/ContentPropertyDisplayMapper.cs index 0a227c4533..41e350bf62 100644 --- a/src/Umbraco.Core/Models/Mapping/ContentPropertyDisplayMapper.cs +++ b/src/Umbraco.Core/Models/Mapping/ContentPropertyDisplayMapper.cs @@ -36,6 +36,8 @@ internal class ContentPropertyDisplayMapper : ContentPropertyBasicMapper( dataTypeService, @@ -42,9 +46,28 @@ public class ContentPropertyMapDefinition : IMapDefinition entityService, textService, loggerFactory.CreateLogger(), - propertyEditors); + propertyEditors, + dataTypeConfigurationCache); } + [Obsolete("Please use constructor that takes an IDataTypeConfigurationCache. Will be removed in V14.")] + public ContentPropertyMapDefinition( + ICultureDictionary cultureDictionary, + IDataTypeService dataTypeService, + IEntityService entityService, + ILocalizedTextService textService, + ILoggerFactory loggerFactory, + PropertyEditorCollection propertyEditors) + : this( + cultureDictionary, + dataTypeService, + entityService, + textService, + loggerFactory, + propertyEditors, + StaticServiceProvider.Instance.GetRequiredService()) + { } + public void DefineMaps(IUmbracoMapper mapper) { mapper.Define>( diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index fcffe4734e..45b3f362dc 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -29,6 +29,7 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IIOHelper _ioHelper; private readonly IDataTypeContainerService _dataTypeContainerService; private readonly IUserIdKeyResolver _userIdKeyResolver; + private readonly Lazy _idKeyMap; public DataTypeService( ICoreScopeProvider provider, @@ -38,7 +39,8 @@ namespace Umbraco.Cms.Core.Services.Implement IDataValueEditorFactory dataValueEditorFactory, IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, - IIOHelper ioHelper) + IIOHelper ioHelper, + Lazy idKeyMap) : base(provider, loggerFactory, eventMessagesFactory) { _dataValueEditorFactory = dataValueEditorFactory; @@ -46,6 +48,7 @@ namespace Umbraco.Cms.Core.Services.Implement _auditRepository = auditRepository; _contentTypeRepository = contentTypeRepository; _ioHelper = ioHelper; + _idKeyMap = idKeyMap; // resolve dependencies for obsolete methods through the static service provider, so they don't pollute the constructor signature _dataTypeContainerService = StaticServiceProvider.Instance.GetRequiredService(); @@ -743,11 +746,11 @@ namespace Umbraco.Cms.Core.Services.Implement } private IDataType? GetDataTypeFromRepository(Guid id) + => _idKeyMap.Value.GetIdForKey(id, UmbracoObjectTypes.DataType) switch { - IQuery query = Query().Where(x => x.Key == id); - IDataType? dataType = _dataTypeRepository.Get(query).FirstOrDefault(); - return dataType; - } + { Success: false } => null, + { Result: var intId } => _dataTypeRepository.Get(intId), + }; private void Audit(AuditType type, int userId, int objectId) => _auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType))); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 85c2b4997b..e0d0bd79fa 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs index 05d6743231..043c4088e9 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; @@ -18,7 +19,7 @@ internal abstract class BlockEditorPropertyValueEditor : BlockV { private readonly IJsonSerializer _jsonSerializer; private BlockEditorValues? _blockEditorValues; - private readonly IDataTypeService _dataTypeService; + private readonly IDataTypeConfigurationCache _dataTypeConfigurationCache; private readonly PropertyEditorCollection _propertyEditors; private readonly DataValueReferenceFactoryCollection _dataValueReferenceFactories; @@ -26,17 +27,17 @@ internal abstract class BlockEditorPropertyValueEditor : BlockV DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories, - IDataTypeService dataTypeService, + IDataTypeConfigurationCache dataTypeConfigurationCache, ILocalizedTextService textService, ILogger> logger, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, IIOHelper ioHelper) - : base(attribute, propertyEditors, dataTypeService, textService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactories) + : base(attribute, propertyEditors, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactories) { _propertyEditors = propertyEditors; _dataValueReferenceFactories = dataValueReferenceFactories; - _dataTypeService = dataTypeService; + _dataTypeConfigurationCache = dataTypeConfigurationCache; _jsonSerializer = jsonSerializer; } @@ -76,7 +77,7 @@ internal abstract class BlockEditorPropertyValueEditor : BlockV continue; } - object? configuration = _dataTypeService.GetDataType(propertyValue.PropertyType.DataTypeKey)?.ConfigurationObject; + object? configuration = _dataTypeConfigurationCache.GetConfiguration(propertyValue.PropertyType.DataTypeKey); foreach (ITag tag in dataValueTags.GetTags(propertyValue.Value, configuration, languageId)) { yield return tag; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockGridPropertyEditorBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockGridPropertyEditorBase.cs index 24ebbb9d82..5bb5393fd0 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockGridPropertyEditorBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockGridPropertyEditorBase.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; @@ -51,7 +52,7 @@ public abstract class BlockGridPropertyEditorBase : DataEditor DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories, - IDataTypeService dataTypeService, + IDataTypeConfigurationCache dataTypeConfigurationCache, ILocalizedTextService textService, ILogger logger, IShortStringHelper shortStringHelper, @@ -59,7 +60,7 @@ public abstract class BlockGridPropertyEditorBase : DataEditor IIOHelper ioHelper, IContentTypeService contentTypeService, IPropertyValidationService propertyValidationService) - : base(attribute, propertyEditors, dataValueReferenceFactories, dataTypeService, textService, logger, shortStringHelper, jsonSerializer, ioHelper) + : base(attribute, propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper) { BlockEditorValues = new BlockEditorValues(new BlockGridEditorDataConverter(jsonSerializer), contentTypeService, logger); Validators.Add(new BlockEditorValidator(propertyValidationService, BlockEditorValues, contentTypeService)); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorBase.cs index e0272fc34b..8cd147913d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditorBase.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; @@ -58,7 +59,7 @@ public abstract class BlockListPropertyEditorBase : DataEditor BlockEditorDataConverter blockEditorDataConverter, PropertyEditorCollection propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories, - IDataTypeService dataTypeService, + IDataTypeConfigurationCache dataTypeConfigurationCache, IContentTypeService contentTypeService, ILocalizedTextService textService, ILogger logger, @@ -66,7 +67,7 @@ public abstract class BlockListPropertyEditorBase : DataEditor IJsonSerializer jsonSerializer, IIOHelper ioHelper, IPropertyValidationService propertyValidationService) : - base(attribute, propertyEditors, dataValueReferenceFactories,dataTypeService, textService, logger, shortStringHelper, jsonSerializer, ioHelper) + base(attribute, propertyEditors, dataValueReferenceFactories, dataTypeConfigurationCache, textService, logger, shortStringHelper, jsonSerializer, ioHelper) { BlockEditorValues = new BlockEditorValues(blockEditorDataConverter, contentTypeService, logger); Validators.Add(new BlockEditorValidator(propertyValidationService, BlockEditorValues, contentTypeService)); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs index 994d7fcbfe..dd62a7d5b9 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockValuePropertyValueEditorBase.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; @@ -13,7 +14,7 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat where TValue : BlockValue, new() where TLayout : class, IBlockLayoutItem, new() { - private readonly IDataTypeService _dataTypeService; + private readonly IDataTypeConfigurationCache _dataTypeConfigurationCache; private readonly PropertyEditorCollection _propertyEditors; private readonly ILogger _logger; private readonly DataValueReferenceFactoryCollection _dataValueReferenceFactoryCollection; @@ -21,7 +22,7 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat protected BlockValuePropertyValueEditorBase( DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, - IDataTypeService dataTypeService, + IDataTypeConfigurationCache dataTypeConfigurationCache, ILocalizedTextService textService, ILogger logger, IShortStringHelper shortStringHelper, @@ -31,7 +32,7 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat : base(textService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _propertyEditors = propertyEditors; - _dataTypeService = dataTypeService; + _dataTypeConfigurationCache = dataTypeConfigurationCache; _logger = logger; _dataValueReferenceFactoryCollection = dataValueReferenceFactoryCollection; } @@ -78,6 +79,7 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat protected IEnumerable GetBlockValueTags(TValue blockValue, int? languageId) { var result = new List(); + // loop through all content and settings data foreach (BlockItemData row in blockValue.ContentData.Concat(blockValue.SettingsData)) { @@ -91,7 +93,7 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat continue; } - object? configuration = _dataTypeService.GetDataType(prop.Value.PropertyType.DataTypeKey)?.ConfigurationObject; + object? configuration = _dataTypeConfigurationCache.GetConfiguration(prop.Value.PropertyType.DataTypeKey); result.AddRange(tagsProvider.GetTags(prop.Value.Value, configuration, languageId)); } @@ -114,7 +116,7 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat private void MapBlockItemDataToEditor(IProperty property, List items) { - var valEditors = new Dictionary(); + var valEditors = new Dictionary(); foreach (BlockItemData row in items) { @@ -136,25 +138,13 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat continue; } - IDataType? dataType = _dataTypeService.GetDataType(prop.Value.PropertyType.DataTypeId); - if (dataType == null) + Guid dataTypeKey = prop.Value.PropertyType.DataTypeKey; + if (!valEditors.TryGetValue(dataTypeKey, out IDataValueEditor? valEditor)) { - // deal with weird situations by ignoring them (no comment) - row.PropertyValues.Remove(prop.Key); - _logger.LogWarning( - "ToEditor removed property value {PropertyKey} in row {RowId} for property type {PropertyTypeAlias}", - prop.Key, - row.Key, - property.PropertyType.Alias); - continue; - } + var configuration = _dataTypeConfigurationCache.GetConfiguration(dataTypeKey); + valEditor = propEditor.GetValueEditor(configuration); - if (!valEditors.TryGetValue(dataType.Id, out IDataValueEditor? valEditor)) - { - var tempConfig = dataType.ConfigurationObject; - valEditor = propEditor.GetValueEditor(tempConfig); - - valEditors.Add(dataType.Id, valEditor); + valEditors.Add(dataTypeKey, valEditor); } var convValue = valEditor.ToEditor(tempProp); @@ -172,7 +162,7 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat foreach (KeyValuePair prop in row.PropertyValues) { // Fetch the property types prevalue - var propConfiguration = _dataTypeService.GetDataType(prop.Value.PropertyType.DataTypeId)?.ConfigurationObject; + var configuration = _dataTypeConfigurationCache.GetConfiguration(prop.Value.PropertyType.DataTypeKey); // Lookup the property editor IDataEditor? propEditor = _propertyEditors[prop.Value.PropertyType.PropertyEditorAlias]; @@ -182,7 +172,7 @@ internal abstract class BlockValuePropertyValueEditorBase : Dat } // Create a fake content property data object - var contentPropData = new ContentPropertyData(prop.Value.Value, propConfiguration); + var contentPropData = new ContentPropertyData(prop.Value.Value, configuration); // Get the property editor to do it's conversion var newValue = propEditor.GetValueEditor().FromEditor(contentPropData, prop.Value.Value); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs index 76b1fde451..77376361e7 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -4,6 +4,7 @@ using System.Text.Json.Nodes; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; @@ -24,7 +25,7 @@ namespace Umbraco.Cms.Core.PropertyEditors; /// internal class ImageCropperPropertyValueEditor : DataValueEditor // TODO: core vs web? { - private readonly IDataTypeService _dataTypeService; + private readonly IDataTypeConfigurationCache _dataTypeConfigurationCache; private readonly IFileStreamSecurityValidator _fileStreamSecurityValidator; private readonly ILogger _logger; private readonly MediaFileManager _mediaFileManager; @@ -42,20 +43,20 @@ internal class ImageCropperPropertyValueEditor : DataValueEditor // TODO: core v IOptionsMonitor contentSettings, IJsonSerializer jsonSerializer, IIOHelper ioHelper, - IDataTypeService dataTypeService, ITemporaryFileService temporaryFileService, IScopeProvider scopeProvider, - IFileStreamSecurityValidator fileStreamSecurityValidator) + IFileStreamSecurityValidator fileStreamSecurityValidator, + IDataTypeConfigurationCache dataTypeConfigurationCache) : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _mediaFileManager = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); _jsonSerializer = jsonSerializer; _contentSettings = contentSettings.CurrentValue; - _dataTypeService = dataTypeService; _temporaryFileService = temporaryFileService; _scopeProvider = scopeProvider; _fileStreamSecurityValidator = fileStreamSecurityValidator; + _dataTypeConfigurationCache = dataTypeConfigurationCache; contentSettings.OnChange(x => _contentSettings = x); Validators.Add(new TemporaryFileUploadValidator(() => _contentSettings, TryParseTemporaryFileKey, TryGetTemporaryFile)); @@ -83,10 +84,10 @@ internal class ImageCropperPropertyValueEditor : DataValueEditor // TODO: core v value = new ImageCropperValue { Src = val.ToString() }; } - IDataType? dataType = _dataTypeService.GetDataType(property.PropertyType.DataTypeId); - if (dataType?.ConfigurationObject != null) + var configuration = _dataTypeConfigurationCache.GetConfigurationAs(property.PropertyType.DataTypeKey); + if (configuration is not null) { - value?.ApplyConfiguration(dataType.ConfigurationAs()); + value?.ApplyConfiguration(configuration); } return value; @@ -213,8 +214,7 @@ internal class ImageCropperPropertyValueEditor : DataValueEditor // TODO: core v } // more magic here ;-( - ImageCropperConfiguration? configuration = _dataTypeService.GetDataType(propertyType.DataTypeId) - ?.ConfigurationAs(); + ImageCropperConfiguration? configuration = _dataTypeConfigurationCache.GetConfigurationAs(propertyType.DataTypeKey); ImageCropperConfiguration.Crop[] crops = configuration?.Crops ?? Array.Empty(); return _jsonSerializer.Serialize(new { src = val, crops }); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs index 29a31f6205..dfb70ba591 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/MediaPicker3PropertyEditor.cs @@ -1,4 +1,5 @@ using System.Text.Json.Nodes; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -48,7 +49,7 @@ public class MediaPicker3PropertyEditor : DataEditor internal class MediaPicker3PropertyValueEditor : DataValueEditor, IDataValueReference { - private readonly IDataTypeService _dataTypeService; + private readonly IDataTypeConfigurationCache _dataTypeReadCache; private readonly IJsonSerializer _jsonSerializer; private readonly IMediaImportService _mediaImportService; private readonly IMediaService _mediaService; @@ -61,21 +62,21 @@ public class MediaPicker3PropertyEditor : DataEditor IJsonSerializer jsonSerializer, IIOHelper ioHelper, DataEditorAttribute attribute, - IDataTypeService dataTypeService, IMediaImportService mediaImportService, IMediaService mediaService, ITemporaryFileService temporaryFileService, IScopeProvider scopeProvider, - IBackOfficeSecurityAccessor backOfficeSecurityAccessor) + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IDataTypeConfigurationCache dataTypeReadCache) : base(shortStringHelper, jsonSerializer, ioHelper, attribute) { _jsonSerializer = jsonSerializer; - _dataTypeService = dataTypeService; _mediaImportService = mediaImportService; _mediaService = mediaService; _temporaryFileService = temporaryFileService; _scopeProvider = scopeProvider; _backOfficeSecurityAccessor = backOfficeSecurityAccessor; + _dataTypeReadCache = dataTypeReadCache; } /// @@ -97,11 +98,9 @@ public class MediaPicker3PropertyEditor : DataEditor var dtos = Deserialize(_jsonSerializer, value).ToList(); dtos = UpdateMediaTypeAliases(dtos); - IDataType? dataType = _dataTypeService.GetDataType(property.PropertyType.DataTypeId); - if (dataType?.ConfigurationObject != null) + var configuration = _dataTypeReadCache.GetConfigurationAs(property.PropertyType.DataTypeKey); + if (configuration is not null) { - MediaPicker3Configuration? configuration = dataType.ConfigurationAs(); - foreach (MediaWithCropsDto dto in dtos) { dto.ApplyConfiguration(configuration); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs index af45287d67..f05ed4c9bd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextPropertyEditor.cs @@ -3,6 +3,8 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; @@ -73,7 +75,7 @@ public class RichTextPropertyEditor : DataEditor public RichTextPropertyValueEditor( DataEditorAttribute attribute, PropertyEditorCollection propertyEditors, - IDataTypeService dataTypeService, + IDataTypeConfigurationCache dataTypeReadCache, ILogger logger, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, ILocalizedTextService localizedTextService, @@ -87,7 +89,7 @@ public class RichTextPropertyEditor : DataEditor IContentTypeService contentTypeService, IPropertyValidationService propertyValidationService, DataValueReferenceFactoryCollection dataValueReferenceFactoryCollection) - : base(attribute, propertyEditors, dataTypeService, localizedTextService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactoryCollection) + : base(attribute, propertyEditors, dataTypeReadCache, localizedTextService, logger, shortStringHelper, jsonSerializer, ioHelper, dataValueReferenceFactoryCollection) { _backOfficeSecurityAccessor = backOfficeSecurityAccessor; _imageSourceParser = imageSourceParser; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs index 31c1e82c77..786d9d056b 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueEditorReuseTests.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models.Blocks; using Umbraco.Cms.Core.PropertyEditors; @@ -41,7 +42,7 @@ public class DataValueEditorReuseTests new BlockListEditorDataConverter(Mock.Of()), _propertyEditorCollection, _dataValueReferenceFactories, - Mock.Of(), + Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of>(),