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
interfacetypes. Here is an abridged summary of the method that finds the template name from the type:Note how:
interfacethat 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
IEnumerableof a simpleinterfaceand setting it to aListof objects of a type implementing aninterfacethat inherits thatinterface. If you do theforeachand@Html.DisplayForon each item, the generic Object template is used.In this case, you have some options:
IRichTextBlock.cshtmltemplate.Object.cshtmltemplate.An example of
IRichTextBlock.cshtmlis this: