v10: Fix Block List settings exception and optimize PVCs (#12342)

* Don't use MapModelType to get model type

* Optimize block list item activation (cache constructors)

* Fix exceptions in NestedContentSingleValueConverter (zero content types or multiple stored items)

* Add IPublishedModelFactory.GetModelType method to remove work-around
This commit is contained in:
Ronald Barendse
2022-05-03 19:23:15 +02:00
committed by GitHub
parent bc49fa5b3f
commit 23072a500c
9 changed files with 254 additions and 155 deletions

View File

@@ -1,6 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Umbraco.Cms.Core.Models.PublishedContent
@@ -59,20 +57,27 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
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);
@@ -89,15 +94,16 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
public IPublishedElement CreateModel(IPublishedElement element)
{
// fail fast
if (_modelInfos == null)
return element;
if (element.ContentType.Alias is null || !_modelInfos.TryGetValue(element.ContentType.Alias, out var modelInfo))
if (_modelInfos is null || element.ContentType.Alias is null || !_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);
@@ -107,21 +113,42 @@ namespace Umbraco.Cms.Core.Models.PublishedContent
public IList? CreateModelList(string? alias)
{
// fail fast
if (_modelInfos == null)
return new List<IPublishedElement>();
if (alias is null || !_modelInfos.TryGetValue(alias, out var modelInfo) || modelInfo.ModelType is null)
if (_modelInfos is null || alias is null || !_modelInfos.TryGetValue(alias, out var modelInfo) || modelInfo.ModelType is null)
{
return new List<IPublishedElement>();
}
var ctor = modelInfo.ListCtor;
if (ctor != null) return ctor();
if (ctor != null)
{
return ctor();
}
var listType = typeof(List<>).MakeGenericType(modelInfo.ModelType);
ctor = modelInfo.ListCtor = ReflectionUtilities.EmitConstructor<Func<IList>>(declaring: listType);
if(ctor is not null) return ctor();
if (ctor is not null)
{
return ctor();
}
return null;
}
/// <inheritdoc />
public Type GetModelType(string? alias)
{
// fail fast
if (_modelInfos is null ||
alias is null ||
!_modelInfos.TryGetValue(alias, out var modelInfo) ||
modelInfo.ModelType is null)
{
return typeof(IPublishedElement);
}
return modelInfo.ModelType;
}
/// <inheritdoc />
public Type MapModelType(Type type)
=> ModelType.Map(type, _modelTypeMap);