Netcore: Migration of Model classes from Umbraco.Infrastructure to Core (#9404)
* Migrating more model, mapping and tree classes * Migrating files from Mapping dir without Newtonsoft dependency * Migrating files from PublishedContent and Editors dirs without Newtonsoft dependency + some more of the same kind * Migrating DataType class without the usage of Newtonsoft.Json and making the corresponding changes to all classes affected * Combining 3 ContentExtensions files into 1 * Refactoring from migrating ContentExtensions * Migrating more classes * Migrating ContentRepositoryExtensions - combining it with existing file in Umbraco.Core * removing Newtonsoft json dependency & migrating file. Adding partial migration of ConfigurationEditor, so PropertyTagsExtensions can be migrated * Migrating ContentTagsExtensions, and refactoring from changes in PropertyTagsExtensions * Changes that should be reverted once ConfigurationEditor class is fully migrated * VS couldn't find Composing, so build was failing. Removing the using solves the problem * Handling a single case for deserializing a subset of an input * Small changes and added tests to JsonNetSerializer Signed-off-by: Bjarke Berg <mail@bergmania.dk> * Migrated ConfigurationEditor Signed-off-by: Bjarke Berg <mail@bergmania.dk> Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
committed by
GitHub
parent
d498c1a2cd
commit
dd5f400cf3
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Provides strongly typed published content models services.
|
||||
/// </summary>
|
||||
public static class PublishedContentExtensionsForModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a strongly typed published content model for an internal published content.
|
||||
/// </summary>
|
||||
/// <param name="content">The internal published content.</param>
|
||||
/// <returns>The strongly typed published content model.</returns>
|
||||
public static IPublishedContent CreateModel(this IPublishedContent content, IPublishedModelFactory publishedModelFactory)
|
||||
{
|
||||
if (publishedModelFactory == null) throw new ArgumentNullException(nameof(publishedModelFactory));
|
||||
if (content == null)
|
||||
return null;
|
||||
|
||||
// get model
|
||||
// if factory returns nothing, throw
|
||||
var model = publishedModelFactory.CreateModel(content);
|
||||
if (model == null)
|
||||
throw new InvalidOperationException("Factory returned null.");
|
||||
|
||||
// if factory returns a different type, throw
|
||||
if (!(model is IPublishedContent publishedContent))
|
||||
throw new InvalidOperationException($"Factory returned model of type {model.GetType().FullName} which does not implement IPublishedContent.");
|
||||
|
||||
return publishedContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a strongly-typed published content.
|
||||
/// </summary>
|
||||
/// <remarks>Every strongly-typed published content class should inherit from <c>PublishedContentModel</c>
|
||||
/// (or inherit from a class that inherits from... etc.) so they are picked by the factory.</remarks>
|
||||
public abstract class PublishedContentModel : PublishedContentWrapped
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedContentModel"/> class with
|
||||
/// an original <see cref="IPublishedContent"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="content">The original content.</param>
|
||||
protected PublishedContentModel(IPublishedContent content)
|
||||
: base(content)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a default implementation for <see cref="IPublishedContentTypeFactory"/>.
|
||||
/// </summary>
|
||||
public class PublishedContentTypeFactory : IPublishedContentTypeFactory
|
||||
{
|
||||
private readonly IPublishedModelFactory _publishedModelFactory;
|
||||
private readonly PropertyValueConverterCollection _propertyValueConverters;
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly object _publishedDataTypesLocker = new object();
|
||||
private Dictionary<int, PublishedDataType> _publishedDataTypes;
|
||||
|
||||
public PublishedContentTypeFactory(IPublishedModelFactory publishedModelFactory, PropertyValueConverterCollection propertyValueConverters, IDataTypeService dataTypeService)
|
||||
{
|
||||
_publishedModelFactory = publishedModelFactory;
|
||||
_propertyValueConverters = propertyValueConverters;
|
||||
_dataTypeService = dataTypeService;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPublishedContentType CreateContentType(IContentTypeComposition contentType)
|
||||
{
|
||||
return new PublishedContentType(contentType, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is for tests and is not intended to be used directly from application code.
|
||||
/// </summary>
|
||||
/// <remarks>Values are assumed to be consisted and are not checked.</remarks>
|
||||
internal IPublishedContentType CreateContentType(Guid key, int id, string alias, Func<IPublishedContentType, IEnumerable<IPublishedPropertyType>> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false)
|
||||
{
|
||||
return new PublishedContentType(key, id, alias, PublishedItemType.Content, Enumerable.Empty<string>(), propertyTypes, variations, isElement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is for tests and is not intended to be used directly from application code.
|
||||
/// </summary>
|
||||
/// <remarks>Values are assumed to be consisted and are not checked.</remarks>
|
||||
internal IPublishedContentType CreateContentType(Guid key, int id, string alias, IEnumerable<string> compositionAliases, Func<IPublishedContentType, IEnumerable<IPublishedPropertyType>> propertyTypes, ContentVariation variations = ContentVariation.Nothing, bool isElement = false)
|
||||
{
|
||||
return new PublishedContentType(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, variations, isElement);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPublishedPropertyType CreatePropertyType(IPublishedContentType contentType, IPropertyType propertyType)
|
||||
{
|
||||
return new PublishedPropertyType(contentType, propertyType, _propertyValueConverters, _publishedModelFactory, this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPublishedPropertyType CreatePropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations = ContentVariation.Nothing)
|
||||
{
|
||||
return new PublishedPropertyType(contentType, propertyTypeAlias, dataTypeId, true, variations, _propertyValueConverters, _publishedModelFactory, this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPublishedPropertyType CreateCorePropertyType(IPublishedContentType contentType, string propertyTypeAlias, int dataTypeId, ContentVariation variations = ContentVariation.Nothing)
|
||||
{
|
||||
return new PublishedPropertyType(contentType, propertyTypeAlias, dataTypeId, false, variations, _propertyValueConverters, _publishedModelFactory, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is for tests and is not intended to be used directly from application code.
|
||||
/// </summary>
|
||||
/// <remarks>Values are assumed to be consisted and are not checked.</remarks>
|
||||
internal IPublishedPropertyType CreatePropertyType(string propertyTypeAlias, int dataTypeId, bool umbraco = false, ContentVariation variations = ContentVariation.Nothing)
|
||||
{
|
||||
return new PublishedPropertyType(propertyTypeAlias, dataTypeId, umbraco, variations, _propertyValueConverters, _publishedModelFactory, this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public PublishedDataType GetDataType(int id)
|
||||
{
|
||||
Dictionary<int, PublishedDataType> publishedDataTypes;
|
||||
lock (_publishedDataTypesLocker)
|
||||
{
|
||||
if (_publishedDataTypes == null)
|
||||
{
|
||||
var dataTypes = _dataTypeService.GetAll();
|
||||
_publishedDataTypes = dataTypes.ToDictionary(x => x.Id, CreatePublishedDataType);
|
||||
}
|
||||
|
||||
publishedDataTypes = _publishedDataTypes;
|
||||
}
|
||||
|
||||
if (!publishedDataTypes.TryGetValue(id, out var dataType))
|
||||
throw new ArgumentException($"Could not find a datatype with identifier {id}.", nameof(id));
|
||||
|
||||
return dataType;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void NotifyDataTypeChanges(int[] ids)
|
||||
{
|
||||
lock (_publishedDataTypesLocker)
|
||||
{
|
||||
if (_publishedDataTypes == null)
|
||||
{
|
||||
var dataTypes = _dataTypeService.GetAll();
|
||||
_publishedDataTypes = dataTypes.ToDictionary(x => x.Id, CreatePublishedDataType);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var id in ids)
|
||||
_publishedDataTypes.Remove(id);
|
||||
|
||||
var dataTypes = _dataTypeService.GetAll(ids);
|
||||
foreach (var dataType in dataTypes)
|
||||
_publishedDataTypes[dataType.Id] = CreatePublishedDataType(dataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PublishedDataType CreatePublishedDataType(IDataType dataType)
|
||||
=> new PublishedDataType(dataType.Id, dataType.EditorAlias, dataType is DataType d ? d.GetLazyConfiguration() : new Lazy<object>(() => dataType.Configuration));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Umbraco.Core.Models.PublishedContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements a strongly typed content model factory
|
||||
/// </summary>
|
||||
public class PublishedModelFactory : IPublishedModelFactory
|
||||
{
|
||||
private readonly Dictionary<string, ModelInfo> _modelInfos;
|
||||
private readonly Dictionary<string, Type> _modelTypeMap;
|
||||
private readonly IPublishedValueFallback _publishedValueFallback;
|
||||
|
||||
private class ModelInfo
|
||||
{
|
||||
public Type ParameterType { get; set; }
|
||||
public Func<object, IPublishedValueFallback, object> Ctor { get; set; }
|
||||
public Type ModelType { get; set; }
|
||||
public Func<IList> ListCtor { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PublishedModelFactory"/> class with types.
|
||||
/// </summary>
|
||||
/// <param name="types">The model types.</param>
|
||||
/// <remarks>
|
||||
/// <para>Types must implement <c>IPublishedContent</c> and have a unique constructor that
|
||||
/// accepts one IPublishedContent as a parameter.</para>
|
||||
/// <para>To activate,</para>
|
||||
/// <code>
|
||||
/// var types = TypeLoader.Current.GetTypes{PublishedContentModel}();
|
||||
/// var factory = new PublishedContentModelFactoryImpl(types);
|
||||
/// PublishedContentModelFactoryResolver.Current.SetFactory(factory);
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public PublishedModelFactory(IEnumerable<Type> types,
|
||||
IPublishedValueFallback publishedValueFallback)
|
||||
{
|
||||
var modelInfos = new Dictionary<string, ModelInfo>(StringComparer.InvariantCultureIgnoreCase);
|
||||
var modelTypeMap = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
// so... the model type has to implement a ctor with one parameter being, or inheriting from,
|
||||
// IPublishedElement - but it can be IPublishedContent - so we cannot get one precise ctor,
|
||||
// we have to iterate over all ctors and try to find the right one
|
||||
|
||||
ConstructorInfo constructor = null;
|
||||
Type parameterType = null;
|
||||
|
||||
foreach (var ctor in type.GetConstructors())
|
||||
{
|
||||
var parms = ctor.GetParameters();
|
||||
if (parms.Length == 2 && typeof(IPublishedElement).IsAssignableFrom(parms[0].ParameterType) && typeof(IPublishedValueFallback).IsAssignableFrom(parms[1].ParameterType))
|
||||
{
|
||||
if (constructor != null)
|
||||
throw new InvalidOperationException($"Type {type.FullName} has more than one public constructor with one argument of type, or implementing, IPublishedElement.");
|
||||
constructor = ctor;
|
||||
parameterType = parms[0].ParameterType;
|
||||
}
|
||||
}
|
||||
|
||||
if (constructor == null)
|
||||
throw new InvalidOperationException($"Type {type.FullName} is missing a public constructor with one argument of type, or implementing, IPublishedElement.");
|
||||
|
||||
var attribute = type.GetCustomAttribute<PublishedModelAttribute>(false);
|
||||
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
|
||||
|
||||
if (modelInfos.TryGetValue(typeName, out var modelInfo))
|
||||
throw new InvalidOperationException($"Both types '{type.AssemblyQualifiedName}' and '{modelInfo.ModelType.AssemblyQualifiedName}' want to be a model type for content type with alias \"{typeName}\".");
|
||||
|
||||
// have to use an unsafe ctor because we don't know the types, really
|
||||
var modelCtor = ReflectionUtilities.EmitConstructorUnsafe<Func<object, IPublishedValueFallback, object>>(constructor);
|
||||
modelInfos[typeName] = new ModelInfo { ParameterType = parameterType, ModelType = type, Ctor = modelCtor };
|
||||
modelTypeMap[typeName] = type;
|
||||
}
|
||||
|
||||
_modelInfos = modelInfos.Count > 0 ? modelInfos : null;
|
||||
_modelTypeMap = modelTypeMap;
|
||||
_publishedValueFallback = publishedValueFallback;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IPublishedElement CreateModel(IPublishedElement element)
|
||||
{
|
||||
// fail fast
|
||||
if (_modelInfos == null)
|
||||
return element;
|
||||
|
||||
if (!_modelInfos.TryGetValue(element.ContentType.Alias, out var modelInfo))
|
||||
return element;
|
||||
|
||||
// ReSharper disable once UseMethodIsInstanceOfType
|
||||
if (modelInfo.ParameterType.IsAssignableFrom(element.GetType()) == false)
|
||||
throw new InvalidOperationException($"Model {modelInfo.ModelType} expects argument of type {modelInfo.ParameterType.FullName}, but got {element.GetType().FullName}.");
|
||||
|
||||
// can cast, because we checked when creating the ctor
|
||||
return (IPublishedElement)modelInfo.Ctor(element, _publishedValueFallback);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList CreateModelList(string alias)
|
||||
{
|
||||
// fail fast
|
||||
if (_modelInfos == null)
|
||||
return new List<IPublishedElement>();
|
||||
|
||||
if (!_modelInfos.TryGetValue(alias, out var modelInfo))
|
||||
return new List<IPublishedElement>();
|
||||
|
||||
var ctor = modelInfo.ListCtor;
|
||||
if (ctor != null) return ctor();
|
||||
|
||||
var listType = typeof(List<>).MakeGenericType(modelInfo.ModelType);
|
||||
ctor = modelInfo.ListCtor = ReflectionUtilities.EmitConstructor<Func<IList>>(declaring: listType);
|
||||
return ctor();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Type MapModelType(Type type)
|
||||
=> ModelType.Map(type, _modelTypeMap);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user