Как стать автором
Обновить

Передача анонимных объектов в View

Время на прочтение3 мин
Количество просмотров3.2K
Автор оригинала: davidebb

Идея состоит в том чтобы пользоваться моделью с новым более удобным dynamic синтаксисом. Главное ограничение тут в том, что нельзя просто передать анонимный объект как модель, потому что анонимные типы имеют модификатор доступа internal.


Предположим, что мы пытаемся написать этот код в контроллере:

public class HomeController : Controller
{
  public ActionResult Index()
  {
    return View(
      new
    {
        Message = "Welcome to ASP.NET MVC!",
        Date = DateTime.Now
      });
  }
}

* This source code was highlighted with Source Code Highlighter.


Обратите внимание, что мы передаем анонимный объект в модель. Главная причина этого — это избежать создание необходимого внешнего ViewModel типа. Очевидно, что это несколько спорный момент, но оно должно выглядеть проще чем такая альтернатива:

ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewData["Date"] = DateTime.Now;
return View();

* This source code was highlighted with Source Code Highlighter.


Затем мы изменяем View таким образом, чтобы он имел
Inherits="System.Web.Mvc.ViewPage<dynamic>"

* This source code was highlighted with Source Code Highlighter.


В идеале это должно позволить нам написать что-то типа этого:

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
  <h2><%= Model.Message %></h2>
  <p>
    The date is <%= Model.Date %>
  </p>
</asp:Content>

* This source code was highlighted with Source Code Highlighter.


Но по умолчанию dynamic binder не даст нам сделать это!

К сожалению, если вы попытаетесь запустить этот код, то будет:
error: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: '<>f__AnonymousType1<string,System.DateTime>.Message' is inaccessible due to its protection level
Причина этого анонимный тип в контроллере является internal, так что он может быть доступен только из той сборки в которой он определен. Так как view компилируется отдельно, dynamic binder жалуется, что не может выйти за пределы этой сборки. Но достаточно просто можно написать свой DynamicObject, который связывает через private reflection.

Здесь мы собираеся написать не только свой DynamicObject, но также свой DynamicViewPage, который использует его. Вот полная реализация:

public class DynamicViewPage : ViewPage {
  // Скрываем модель класса предка и заменяем его на dynamic
  public new dynamic Model { get; private set; }

  protected override void SetViewData(ViewDataDictionary viewData) {
    base.SetViewData(viewData);

    // Создаем динамический объект, который использует private рефлексию над объектом модели
    Model = new ReflectionDynamicObject() { RealObject = ViewData.Model };
  }

  class ReflectionDynamicObject : DynamicObject {
    internal object RealObject { get; set; }

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
      // Возвращаем значение свойства
      result = RealObject.GetType().InvokeMember(
        binder.Name,
        BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
        null,
        RealObject,
        null);

      // Всегда возвращаем true, так как InvokeMember вызовет исключение, если что-то пойдет не так
      return true;
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Как вы видите здесь все просто. Все что нам нужно это узнать значение свойства используя private reflection. Вот и все, осталось только использовать это как наш базовый класс для View.
<%@ Page Language=«C#» MasterPageFile="~/Views/Shared/Site.Master" Inherits=«MvcHelpers.DynamicViewPage» %>

Теги:
Хабы:
Всего голосов 26: ↑16 и ↓10+6
Комментарии6

Публикации

Истории

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
19 сентября
CDI Conf 2024
Москва
20 – 22 сентября
BCI Hack Moscow
Москва
24 сентября
Конференция Fin.Bot 2024
МоскваОнлайн
24 сентября
Astra DevConf 2024
МоскваОнлайн
25 сентября
Конференция Yandex Scale 2024
МоскваОнлайн
28 – 29 сентября
Конференция E-CODE
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
30 сентября – 1 октября
Конференция фронтенд-разработчиков FrontendConf 2024
МоскваОнлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн