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,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