1

我有一个具有属性的视图模型

public IRichTextContent Body { get; set; }

此接口继承,并且IEnumerable<IRichTextBlock>有 3 个接口继承IRichTextBlockIHtmlContent和。这些都是Kentico Kontent Delivery .NET SDK的一部分。此属性的正常推荐渲染方法是:IInlineImageIInlineContentItem

@Html.DisplayFor(vm => vm.Body)

一切正常。对于IInlineImageIHtmlContent类型,解决方案中没有显示模板,ASP.NET MVCToString()对其调用方法。如果我在解决方案中放置类型的显示模板,那么这些模板将被拾取并使用。具有类型 object的IInlineContentItem属性,可以保存各种实际类型,并且 ASP.NET MVC 正确解析此对象的正确显示模板,可能是由于IEnumerable<object>实现(请参阅 InlineContentItem)。快乐的日子到目前为止,元数据模板解析小精灵的神奇作品。

在某些情况下,我希望能够使用不同的显示模板,因此该类型的单个显示模板将不起作用。由于模型属性是不同类型的集合,因此我无法按原样执行此操作。所以我想我会枚举IEnumerable<IRichTextBlock>然后DisplayFor()在需要的地方调用传递模板的类型。像这样的东西:

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

对于我指定模板的情况,这工作正常,正确的类型被发送到模板。但是,没有模板的默认 switch case 现在不能解析ToString()我的解决方案中的基础类型或显示模板。相反,似乎默认的 ASP.NET MVC 对象模板IHtmlContent用于IInlineContentItem.

在枚举集合本身时 ASP.NET MVC 正确解析基础类型的情况与我这样做的情况有什么区别?人们通常似乎不会对集合上的 foreach 有问题,但我认为这里的问题是多态性?

4

1 回答 1

1

您的假设是正确的:基于 ASP.NET Core MVC 源,区别在于多态性,或者特别是模板解析不处理interface类型的继承。以下是从 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";
        }

注意如何:

  1. 除一种特殊情况外,返回运行时类型名称。
  2. 当类型不是interface返回层次结构中的类型名称时,存在一个条件。
  3. 没有别的,直到"Object"作为通用模板名称返回的结尾。

IEnumerable无论 Kentico Kontent Delivery .NET SDK 是什么,都会发生这种情况,您可以通过使用一个简单的创建模型属性interface并将其设置为实现继承该List的类型的对象来测试它。如果对每个项目执行and ,则使用通用对象模板interfaceinterfaceforeach@Html.DisplayFor

在这种情况下,您有一些选择:

  • 始终传入模板名称(甚至创建扩展方法以自动从运行时类型中提取它)。
  • 实现IRichTextBlock.cshtml模板。
  • 实现Object.cshtml模板。

一个例子 IRichTextBlock.cshtml是这样的:

@model Kentico.Kontent.Delivery.Abstractions.IRichTextBlock

@switch (Model)
{
  case Kentico.Kontent.Delivery.Abstractions.IInlineContentItem inlineContentItem:
    // Render inlineContentItem
    break;
  default:
    @Html.Raw(Model.ToString())
    break;
}
于 2020-04-13T19:39:29.190 回答