diff --git a/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs index 9e488dd797..25d9643bed 100644 --- a/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/IPublishedModelFactory.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using System.Collections; namespace Umbraco.Core.Models.PublishedContent { @@ -17,9 +17,18 @@ namespace Umbraco.Core.Models.PublishedContent IPublishedElement CreateModel(IPublishedElement element); /// - /// Gets the model type map. + /// Creates a List{T} of a strongly-typed model for a model type alias. /// - /// The model type map maps element type aliases to actual Clr types. - Dictionary ModelTypeMap { get; } + /// The model type alias. + /// A List{T} of the strongly-typed model, exposed as an IList. + IList CreateModelList(string alias); + + /// + /// Maps a Clr type that may contain model types, to an actual Clr type. + /// + /// The Clr type. + /// The actual Clr type. + /// See for more details. + Type MapModelType(Type type); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedModelFactory.cs index 1ffbc04fe2..77001f43ed 100644 --- a/src/Umbraco.Core/Models/PublishedContent/NoopPublishedModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/NoopPublishedModelFactory.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; namespace Umbraco.Core.Models.PublishedContent @@ -11,6 +12,9 @@ namespace Umbraco.Core.Models.PublishedContent public IPublishedElement CreateModel(IPublishedElement element) => element; /// - public Dictionary ModelTypeMap { get; } = new Dictionary(); + public IList CreateModelList(string alias) => new List(); + + /// + public Type MapModelType(Type type) => typeof(IPublishedElement); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedModelFactory.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedModelFactory.cs index 392508f591..8af3d013b0 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedModelFactory.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedModelFactory.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Reflection; @@ -10,16 +11,16 @@ namespace Umbraco.Core.Models.PublishedContent public class PublishedModelFactory : IPublishedModelFactory { private readonly Dictionary _modelInfos; + private readonly Dictionary _modelTypeMap; private class ModelInfo { public Type ParameterType { get; set; } public Func Ctor { get; set; } public Type ModelType { get; set; } + public Func ListCtor { get; set; } } - public Dictionary ModelTypeMap { get; } - /// /// Initializes a new instance of the class with types. /// @@ -37,7 +38,7 @@ namespace Umbraco.Core.Models.PublishedContent public PublishedModelFactory(IEnumerable types) { var modelInfos = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - ModelTypeMap = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + var modelTypeMap = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach (var type in types) { @@ -72,19 +73,21 @@ namespace Umbraco.Core.Models.PublishedContent // have to use an unsafe ctor because we don't know the types, really var modelCtor = ReflectionUtilities.EmitCtorUnsafe>(constructor); modelInfos[typeName] = new ModelInfo { ParameterType = parameterType, ModelType = type, Ctor = modelCtor }; - ModelTypeMap[typeName] = type; + modelTypeMap[typeName] = type; } _modelInfos = modelInfos.Count > 0 ? modelInfos : null; + _modelTypeMap = modelTypeMap; } + /// public IPublishedElement CreateModel(IPublishedElement element) { // fail fast if (_modelInfos == null) return element; - if (_modelInfos.TryGetValue(element.ContentType.Alias, out var modelInfo) == false) + if (!_modelInfos.TryGetValue(element.ContentType.Alias, out var modelInfo)) return element; // ReSharper disable once UseMethodIsInstanceOfType @@ -94,5 +97,27 @@ namespace Umbraco.Core.Models.PublishedContent // can cast, because we checked when creating the ctor return (IPublishedElement) modelInfo.Ctor(element); } + + /// + public IList CreateModelList(string alias) + { + // fail fast + if (_modelInfos == null) + return new List(); + + if (!_modelInfos.TryGetValue(alias, out var modelInfo)) + return new List(); + + var ctor = modelInfo.ListCtor; + if (ctor != null) return ctor(); + + var listType = typeof(List<>).MakeGenericType(modelInfo.ModelType); + ctor = modelInfo.ListCtor = ReflectionUtilities.EmitCtor>(declaring: listType); + return ctor(); + } + + /// + public Type MapModelType(Type type) + => ModelType.Map(type, _modelTypeMap); } } diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs index 6f51dc4e0d..026d1ef1f8 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs @@ -280,7 +280,7 @@ namespace Umbraco.Core.Models.PublishedContent get { if (!_initialized) Initialize(); - return _clrType ?? (_clrType = ModelType.Map(_modelClrType, Current.PublishedModelFactory.ModelTypeMap)); + return _clrType ?? (_clrType = Current.PublishedModelFactory.MapModelType(_modelClrType)); } } diff --git a/src/Umbraco.Tests/Facade/NestedContentTests.cs b/src/Umbraco.Tests/Facade/NestedContentTests.cs index 2ccba2e58d..0397bfa4c7 100644 --- a/src/Umbraco.Tests/Facade/NestedContentTests.cs +++ b/src/Umbraco.Tests/Facade/NestedContentTests.cs @@ -71,12 +71,13 @@ namespace Umbraco.Tests.Facade container.RegisterSingleton(f => publishedModelFactory.Object); // mocked model factory returns model type + var modelTypes = new Dictionary + { + { "contentN1", typeof(TestElementModel) } + }; publishedModelFactory - .Setup(x => x.ModelTypeMap) - .Returns(new Dictionary - { - { "contentN1", typeof (TestElementModel) } - }); + .Setup(x => x.MapModelType(It.IsAny())) + .Returns((Type type) => ModelType.Map(type, modelTypes)); // mocked model factory creates models publishedModelFactory diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs index 8b68e42a58..441b3bc61e 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/NestedContentManyValueConverter.cs @@ -1,11 +1,8 @@ using System; -using System.Collections; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; @@ -19,7 +16,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// public class NestedContentManyValueConverter : NestedContentValueConverterBase { - private readonly ConcurrentDictionary> _listCtors = new ConcurrentDictionary>(); private readonly ProfilingLogger _proflog; /// @@ -70,26 +66,9 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters // fixme do NOT do it here! + use the facade cache var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId); var contentTypes = preValueCollection.PreValuesAsDictionary["contentTypes"].Value; - IList elements; - if (contentTypes.Contains(",")) - { - elements = new List(); - } - else if (PublishedModelFactory.ModelTypeMap.TryGetValue(contentTypes, out var type)) - { - var ctor = _listCtors.GetOrAdd(type, t => - { - var listType = typeof(List<>).MakeGenericType(t); - return ReflectionUtilities.EmitCtor>(declaring: listType); - }); - - elements = ctor(); - } - else - { - // should we throw instead? - elements = new List(); - } + var elements = contentTypes.Contains(",") + ? new List() + : PublishedModelFactory.CreateModelList(contentTypes); foreach (var sourceObject in objects) {