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:
Elitsa Marinovska
2020-11-17 20:27:10 +01:00
committed by GitHub
parent d498c1a2cd
commit dd5f400cf3
116 changed files with 1098 additions and 897 deletions

View File

@@ -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;
}
}
}

View File

@@ -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)
{ }
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}