Испуская строку HTML из свойства анонимного типа в повторном, динамически типизированном частичном представлении

Я передаю анонимный тип в динамическое частичное представление как часть @model, а одно из свойств - это строка, содержащая некоторый HTML. Когда я использую методы HtmlHelper для рендеринга этого свойства, механизм Razor кодирует строку, в результате чего в этом случае вместо символа text создается буквальный текст на странице - <i>text</i>.

Поскольку это динамически типизированный вид, я не могу напрямую вызвать это свойство. В частности, если я пытаюсь привязать к @Model.MyField, я получаю a RuntimeBindingException:

'object' does not contain a definition for 'MyField'

В идеале я мог бы создать тип (или, по крайней мере, интерфейс) для указания для представления (который я рекомендую в качестве оптимального решения), но мой объем работы не позволяет этого. Кроме того, я использую Partial View в первую очередь, чтобы я мог перерабатывать шаблон для разных типов, которые имеют одинаковые имена свойств, но не для тех же типов (yay legacy code!).

Я рассмотрел несколько связанных вопросов, которые затрагивают аналогичные проблемы, но ответы не работают для моей конкретной ситуации (из-за необходимости анонимного типа, переданного моему представлению @model dynamic)).

Попытки решения

  • Хорошо, поэтому я предполагаю, что я просто поменяю свойства String на один из этих свойств IHtmlString... nope. Поскольку у меня есть анонимный тип, движок Razor не знает, что MyField является IHtmlString и (я предполагаю) вызывает .ToString(), который затем кодируется как обычно.

  • Хорошо, может быть, @Html.DisplayFor умнее? Да, но доступ запрещен:

    'System.Web.Mvc.HtmlHelper' не имеет применимого метода с именем "DisplayFor", но по этому имени имеет метод расширения. Методы расширения не могут динамически отправляться. Подумайте о том, как использовать динамические аргументы или вызывать метод расширения без синтаксиса метода расширения.

    О, верно. Динамически отправляется - я не могу называть методы расширения анонимными методами, потому что Razor не знает, что это такое. Потому что я использую @model dynamic, чтобы явным образом сказать это, стиль джедая: "Вам не нужно видеть его идентификацию". Если бы я всегда знал, какой именно тип, то да, я мог бы наложить объект или вызвать метод расширения без использования синтаксиса, но опять же, dynamic и anonymous. Вид курицы и яиц здесь.

Ответ 1

Я нашел/скомпилировал два решения, ни один из которых я действительно не доволен. YMMV:

  • Установите ViewBag.MyField в родительском View перед рендерингом каждого Partial View.

    Хорошо, мне следовало бы рассказать об этом намного раньше, но мне пришлось напомнить эту возможность здесь, потому что я редко ее использую (предпочитая строго типизированные представления как только возможно). Я на самом деле попытался это сделать на ранней стадии, но из-за того, как я делаю Partial несколько раз, это казалось нецелесообразным. Мне все равно это не нравится, потому что в родительском представлении я должен постоянно обновлять ViewBag.MyField перед каждым вызовом @Html.Partial (6 раз для моего варианта использования). Это ставит код С# и повторное использование переменной вниз по странице в середине моего контента, где ее легко пропустить и трудно поддерживать.

  • Использовать отражение: object myField = ((Type)Model.GetType()).GetProperty("MyField").GetValue(Model);

    В конечном итоге я решил пойти на мой вариант использования. Хотя отражение не предназначено для этого сценария, хотя для этого требуется небольшая дополнительная проверка ошибок. Люди, которые будут поддерживать это, более знакомы с рефлексией, чем .NET MVC, и объединяют код в одно место - на той странице, на которую оно имеет значение, и вверху с остальными "серверными" манипуляциями. Нет повторных вызовов или поиска ссылок.

    Я не совсем понимаю, почему это работает (также работает с dynamic вместо object), но я предполагаю, что это что-то связано с движком Razor, проверяющим тип объекта myField непосредственно для специального рендеринга известного типа (IHtmlString), вместо того, чтобы видеть неизвестный object и ему нужно получить доступ к свойству, которое, как известно, не существует во время компиляции.