Это вторая статья из серии посвященная DotVVM. Первая статья была скорее ознакомительной. Я старался на простом примере показать как работать в DotVVM на базовом уровне. Статья, по сути, не затрагивала самого важного: как это работает.

Этому вопросу а также оптимизации трафика посвящена эта статья.

В этой статье рассмотрим более детально коммуникацию между клиентом и сервером.
Можно взять пример из предыдущей статьи со списком дел. В приложение, написанное там, можно было добавлять новые дела и помечать их как сделанные.

Коммуникация


При запросе на страницу, происходит парсинг URL и DotVVM ищет его в DotVVMStartup.cs, где в таблице маршрутов есть путь на .dothtml файл, где, в свою очередь, есть директива на ViewModel.

ViewModel проходит сериализацию в json и это накладывает на ViewModel определённые правила. В json попадают все публичные методы и публичные свойства (string, Guid, bool, int остальные числовые типы, DateTime, коллекции (array, List). В случае List, это может быть коллекция таких же простых типов, или объектов, которые эти типы используют.

При работе с DotVVM в реальных проектах выявились несколько правил для архитектуры ViewModel, которых мы стараемся придерживаться.

  • Не использовать Entity Framework DbContext прямо в ViewModel
  • Не показывать во View чистые Entity, а использовать DTO (Data Transfer Objects)
  • Для большего контроля над ViewModel, он должен наследовать DotvvmViewModelBase

Итак представим, что ViewModel успешно собрался в json, во View сгенерировались все front-end привязки и страница загрузилась. Поочередно прошли методы, наследуемые от DotvvmViewModelBase: Init(), Load() и PreRender(). При первой загрузке страницы, override этих функций может быть полезен, но обо всем по порядку.



Добавим новое дело с список.

  1. На сервер посылается AJAX POST с json изменённого ViewModel
  2. В память сервера выгружается тот же ViewModel, но неизмененный
  3. Произведён Init()
  4. Неизмененный серверный ViewModel сравнивается с тем, что пришло с клиента
  5. Произведён Load()
  6. Происходит выполнение самого метода
  7. Произведён PreRender()
  8. После сравнения изменения посылаются обратно на клиента.

Когда ваш ViewModel наследует DotvvmViewModelBase у вас есть доступ к Init(), Load() и PreRender(). Все три метода абстрактные и их можно расширять и модифицировать.

Также в DotvvmViewModelBase есть свойство контекста запроса Context, откуда есть доступ к объекту запроса, к свойству IsPostBack, к параметрам URL много чему еще.

Посылать при каждом постбэке весь ViewModel весьма не эффективно. Для того, чтобы хоть как-то снизить количество пересылаемых данных, в DotVVM есть несколько подходов.

Атрибут Bind


Атрибут Bind позволяет указать компилятору, как обрабатывать свойства в ViewModel.



  • [Bind(Direction.Both)] — Настройка по умолчанию. Данные посылаются при каждом запросе.
  • [Bind(Direction.None)] — В основном используется для сервисов и фасад, которые не проходят сериализацию, но используются в ViewModel для загрузки или сохранения данный.
  • [Bind(Direction.ServerToClient)] — Данные посылаютя только в одну сторону, с сервера на клиент. Значит только при загрузке страницы, но не при постбэке.
  • [Bind(Direction.ServerToClientFirstRequest)] — Идеально подходит для статичных данных. Например вариантов в комбинированном списке
  • [Bind(Direction.ClientToServer)] — Данные посылаются только в одну сторону, при постбэке, с клиента на сервер.

Static commands


DotVVM также позволяет сделать запрос на статический метод на сервере. Эффективность таких методов в том, что они посылают только ответ, то есть с сервера приходит весь ViewModel, а только нужная нам часть. Такие статические методы могут принимать параметры и возвращать значения.

В нашем примере есть место, где можно применить Static commands. Когда мы помечаем дело как „сделанное“. Не обязательно при этом посылать весь ViewModel на сервер.
Можно переписать метод MarkAsDone(ToDoItem item) на статический перенеся его в отдельный статический класс и добавив к методу атрибут [AllowStaticCommand].

namespace FirstDotvvmApp
{
   public static class ToDoItemValidator
   {
        [AllowStaticCommand]
        public static bool IsDone() => true;
   }
}

Нужно также изменить View добавив директиву с пространством имен, где находится наш статический класс.

@import FirstDotvvmApp

Кнопка будет использовать не просто command, а staticCommand.

<dot:Button
Validation.Enabled="false"
Visible="{value: !IsDone}"
Text="Mark as done"
Click="{staticCommand: IsDone = ToDoItemValidator.IsDone()}">
</dot:Button>

Для сравнения можно посмотреть сколько трафика мы сэкономили.



Обычно для экономии трафика при коммуникации на реальный проектах используется комбинация обоих подходов.

Ссылки


Больше примеров по использованию новых функций можно найти по ссылке.

Также недавно прошел стрим (анг.) по DotVVM 2.0, где рассказали про главные нововведения и новые функции.

Комментарии (0)