ASP. NET MVC Базовый дисплей Не отображать правильный тип при перечислении полиморфиев c IEnumerable <T> - PullRequest
1 голос
/ 05 апреля 2020

У меня есть модель представления, которая имеет свойство

public IRichTextContent Body { get; set; }

Этот интерфейс наследует IEnumerable<IRichTextBlock>, и есть 3 интерфейса, которые наследуют IRichTextBlock: IHtmlContent, IInlineImage и IInlineContentItem , Все это входит в комплект поставки Kentico Kontent. NET SDK . Обычный рекомендуемый метод рендеринга для этого свойства:

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

и все работает отлично. Для типов IInlineImage и IHtmlContent без шаблонов отображения в решении ASP. NET MVC вызывает для них метод ToString(). Если я размещу шаблоны отображения для типов в решении, они будут выбраны и использованы. IInlineContentItem имеет свойство типа object, которое может содержать различные фактические типы, и ASP. NET MVC правильно разрешает правильный шаблон отображения для этого объекта, предположительно из-за реализации IEnumerable<object> (см. ). InlineContentItem ). Счастливые дни пока, разрешение шаблона метаданных pix ie magi c работает.

В некоторых сценариях ios Я хочу иметь возможность использовать различные шаблоны отображения, поэтому один шаблон отображения для Тип не будет работать. Поскольку свойство модели представляет собой коллекцию разных типов, я не могу сделать это в том виде, в каком оно есть. Поэтому я решил, что перечислю 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;
    }
}

Для случая, когда я указываю шаблон, все работает нормально, правильный шаблон отправляется в шаблон. Однако случай переключения по умолчанию без шаблона теперь не разрешает ни базовый тип ToString(), ни шаблоны отображения в моем решении. Вместо этого кажется, что шаблон ASP. NET MVC по умолчанию используется для IHtmlContent, а для IInlineContentItem.

ничего не отображается. Какая здесь разница между случаем, когда ASP. NET MVC правильно разрешает базовые типы при перечислении самой коллекции и случая, когда я делаю это? Люди обычно не имеют проблем с foreach над коллекцией, но я предполагаю, что проблема заключается в полиморфизме?

1 Ответ

1 голос
/ 13 апреля 2020

Ваше предположение верно: на основе источника ASP. NET Core MVC различие заключается в полиморфизме или, в частности, в том, что разрешение шаблона не обрабатывает наследование типов interface. Вот сокращенное резюме метода, который находит имя шаблона из типа :

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" возвращается как шаблонное имя c.

Это происходит независимо от доставки Kentico Kontent. NET SDK, и вы можете проверить его, создав свойство модели с помощью IEnumerable простого interface и присвоение ему List объектов типа, реализующего interface, который наследует это interface. Если вы делаете foreach и @Html.DisplayFor для каждого элемента, используется шаблон c * * Шаблон объекта .

В этом случае у вас есть несколько вариантов:

  • Всегда передавайте имя шаблона (даже создавайте метод расширения для автоматического извлечения его из типа времени выполнения).
  • Реализация шаблона IRichTextBlock.cshtml.
  • Реализация Object.cshtml template.

Пример 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...