I am have a view model which has a property
public IRichTextContent Body { get; set; }
This interface inherits IEnumerable<IRichTextBlock>
and there are 3 interfaces that inherit IRichTextBlock
: IHtmlContent
, IInlineImage
and IInlineContentItem
. These are all part of the Kentico Kontent Delivery .NET SDK . The normal recommended rendering approach for this property is:
@Html.DisplayFor(vm => vm.Body)
and everything works fine. For the IInlineImage
and IHtmlContent
types, with no display templates in the solution, ASP.NET MVC calls the ToString()
method on them. If I place display templates for the types in the solution then these are picked up and used. The IInlineContentItem
has a property of type object that can hold various actual types and ASP.NET MVC correctly resolves the right display template for this object, presumably due to the IEnumerable<object>
implementation ( see the InlineContentItem ). Happy days so far, the meta data template resolution pixie magic works.
In some scenarios I want to be able to use different display templates, so a single display template for the type will not work. As the model property is a collection of different types I can't do this as it stands. So I figured I would enumerate the IEnumerable<IRichTextBlock>
and then call DisplayFor()
on the types passing a template where required. Something like this:
@foreach (var block in Model.Body)
{
@switch (block)
{
case Kentico.Kontent.Delivery.Abstractions.IInlineImage image:
@Html.DisplayFor(vm => image, "AmpInlineImage")
break;
default:
@Html.DisplayFor(vm => block)
break;
}
}
For the case where I specify the template this works fine, the correct type is sent to the template. However, the default switch case without a template now does not resolve either the underlying type ToString()
or the display templates in my solution. Instead it seems the default ASP.NET MVC object template is used for the IHtmlContent
and nothing is rendered for the IInlineContentItem
.
What is the difference here between the case where ASP.NET MVC correctly resolves the underlying types when enumerating the collection itself and the case where I am doing this? People do not normally seem to have issues with a foreach over a collection, but I presume the issue here is the polymorphism?
Your presumption is correct: based on the ASP.NET Core MVC source, the difference is the polymorphism, or specifically that template resolution does not handle inheritance of interface
types. Here is an abridged summary of the method that finds the template name from the type :
public static IEnumerable<string> GetTypeNames(ModelMetadata modelMetadata, Type fieldType)
{
// ...
var fieldTypeInfo = fieldType.GetTypeInfo();
if (typeof(IEnumerable<IFormFile>) != fieldType)
{
yield return fieldType.Name;
}
if (fieldType == typeof(string))
{
// ...
}
else if (!modelMetadata.IsComplexType)
{
// A complex type is defined as a Type without a
// TypeConverter that can convert from string
}
else if (!fieldTypeInfo.IsInterface)
{
var type = fieldType;
while (true)
{
type = type.GetTypeInfo().BaseType;
if (type == null || type == typeof(object))
{
break;
}
yield return type.Name;
}
}
if (typeof(IEnumerable).IsAssignableFrom(fieldType))
{
if (typeof(IEnumerable<IFormFile>).IsAssignableFrom(fieldType))
{
// ...
}
yield return "Collection";
}
else if (typeof(IFormFile) != fieldType && typeof(IFormFile).IsAssignableFrom(fieldType))
{
yield return nameof(IFormFile);
}
yield return "Object";
}
Note how:
interface
that returns the type names in the hierarchy."Object"
is returned as a generic template name. This occurs regardless of the Kentico Kontent Delivery .NET SDK, and you can test it by creating a model property using an IEnumerable
of a simple interface
and setting it to a List
of objects of a type implementing an interface
that inherits that interface
. If you do the foreach
and @Html.DisplayFor
on each item, the generic Object template is used.
In this case, you have some options:
IRichTextBlock.cshtml
template.Object.cshtml
template. An example of IRichTextBlock.cshtml
is this:
@model Kentico.Kontent.Delivery.Abstractions.IRichTextBlock
@switch (Model)
{
case Kentico.Kontent.Delivery.Abstractions.IInlineContentItem inlineContentItem:
// Render inlineContentItem
break;
default:
@Html.Raw(Model.ToString())
break;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.