Урок 21. Автоматическое обновление данных

Если хотите начать практику с этого урока, то вам необходимо развернуть учебный проект по инструкции в статье Разворачивание проекта.

При разворачивании проекта используйте backup базы данных, который можете найти в архиве из раздела Ответы прошлого урока. Скопируйте папки Forms, Workflow и Patterns в папку с развернутым проектом, например, в папку D:\WT\Projects\Template\Projects\1. Template.

Инструкция по подключению шаблонов находится по ссылке.

В многопользовательском режиме всем пользователям важно оперативно получать информацию о действиях других пользователей в программе. Например, если кто-то создал заказ, то все пользователи должны увидеть его у себя на главной форме без необходимости перезагружать или обновлять форму. Для этого в WT-платформе есть несколько способов автоматического обновления данных, получаемых от сервера:

  • Механизм автоматического обновления у самого PrimaryGetDataConnection, где через вложенный тэг <UpdateInterval> задается интервал отправки запроса на сервер;

  • Использование объекта Timer, который выполняет последовательность команд с заданным интервалом. Можно контролировать время запуска таймера, задав конкретное время старта, либо использовать ручной запуск таймера. Так же можно контролировать количество выполняемых циклов до завершения работы таймера;

  • Подписка формы на событие, которое рассылает сервер всем клиентским частям. В этом случае сервер отслеживает изменения данных и рассылает уведомление о событии. Клиентские части поймав уведомление проверяют, какие из открытых форм подписаны на пришедшее событие, и запускают на этих формах соответствующие обработчики.

UpdateInterval

PrimaryGetDataConnection имеет необязательный вложенный тэг <UpdateInterval>, который включает механизм автоматического обновления данных и задает интервал обновлений.

Перейдем в файл стартовой формы и в описание OrderPrimaryGetDataConnection добавим вложенный тэг:

<UpdateInterval Seconds="20">True</UpdateInterval>

Таким образом, мы задаем интервал обновления в 20 секунд, и DataConnection уже самостоятельно следит за выполнением обновлений.

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

У этого способа есть минус: несмотря на то, что у OrderPrimaryGetDataConnection стоит тэг <ManualLoad> со значением True, соединение с данными все равно будет отправлять запрос на сервер. И пока пользователь не прошел аутентификацию, запрос на сервер будет подписываться пользователем WS_GUEST, у которого недостаточно прав на выполнение запроса OrderSelectSqlQuery.

Уберем внесенные изменения.

Timer

В WT-платформе реализован объект Timer, выполняющий набор команд с заданным интервалом. Этот объект можно использовать для обновления данных в таблице. Например, вместо подписки главной формы на событие, можно с помощью объекта Timer задать интервал выполнения команды OrderDataConnectionRefreshCommand.

Давайте посмотрим, как это можно реализовать.

Перейдем в файл стартовой формы и создадим объект типа Timer:

TemplateStart.xml
<MyObject Name="Timer" Type="Timer" Assembly="SimpleControls">
  <ManualStart>True</ManualStart>
  <Interval>20000</Interval>
  <Commands>
    <Command Name="OrderDataConnectionRefreshCommand" />
  </Commands>
</MyObject>

В тэге <Commands> прописали вызов команды на обновление данных о заказах. А в качестве интервала для тестов указали 20 секунды.

Обратите внимание на тэг <ManualStart> со значением True. Это необходимо, чтобы таймер не стартовал автоматически во время запуска формы, т.к. пользователь еще не прошел аутентификацию.

Создадим команду типа ValueSetCommand, которая будет запускать таймер, используя его set-проперти Start:

<Command Name="StartTimerValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="Timer">
    <Property Name="Start" />
  </Object>
</Command>

Добавим эту команду в Execution по условию LoginFormShowCommand.

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

Минус такого обновления данных в том, что клиентская часть будет всегда обращаться на сервер за данными, даже если данные не изменились.

Уберем внесенные изменения.

Рассылка событий

Самым лучшим способом обновления данных является механизм подписки форм на событие, которое генерирует серверная часть при выполнении запроса на изменении данных в базе. Механизм состоит из двух частей: генерация события сервером и подписка формы на событие.

Генерация события

Для тэга <SqlQuery> существует вложенный тэг <Events>, содержащий список событий, которые будут переданы на все клиентские приложения после выполнения запроса.

Таким образом, сервер может генерировать несколько событий одновременно, и на каждое событие могут быть подписаны разные формы.

Каждое событие, описываемое тэгом <Event>, содержит набор параметров, передаваемых на форму, подписанную на это событие.

Давайте для запроса OrderUpdateSqlQuery добавим событие ChangedNumberOfOrders:

TemplateStart.xml
<SqlQuery Name="OrderUpdateSqlQuery">
  <Events>
    <Event Name="ChangedNumberOfOrders">
      <Parameters>
        <Parameter Name="NumberOfOrders">
          SELECT
            count(*)
          FROM
            template.order O
          WHERE
            O.added AND
            NOT O.deleted;
        </Parameter>
      </Parameters>
    </Event>
  </Events>
  <Text>
    UPDATE template.order
    SET
      order_number = {OrderNumber},
      order_date = {OrderDate},
      client_id = {ClientId},
      description = {Description},
      added = true
    WHERE
      order_id = {OrderId};
  </Text>
</SqlQuery>

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

Подписание формы на событие

Чтобы подписать форму на какое-либо событие, необходимо создать объект типа Subscriber, в котором указать имя события или имена нескольких событий.

Давайте перейдем в файл стартовой формы (TemplateStart.xml) и подпишем ее на добавленное ранее событие:

TemplateStart.xml
<MyObject Name="NumberOfOrdersSubscriber" Type="Subscriber" Assembly="SimpleControls">
  <Subscribes>
    <Subscribe>ChangedNumberOfOrders</Subscribe>
  </Subscribes>
</MyObject>

Теперь необходимо проверять, что событие сработало. Для этого воспользуемся get-проперти Subscribe, которое возвращает название события последнего принятого сообщения:

TemplateStart.xml
<Condition Name="NumberOfOrdersSubscriberChangedCondition" Type="ChangedCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="NumberOfOrdersSubscriber">
        <Property Name="Subscribe" />
      </Object>
    </Item>
  </Items>
</Condition>

Так как у нас только одно событие, на которое подписана главная форма, то достаточно использовать условие ChangedCondition.

Если бы главная форма была подписана на несколько событий, то мы бы использовали EqualCondition, чтобы сопоставлять значение get-проперти с именами событий:

<Condition Name="FirstEventNameSubscriberEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="Subscriber">
        <Property Name="Subscribe" />
      </Object>
    </Item>
    <Item>FirstEventName</Item>
  </Items>
</Condition>

<Condition Name="SecondEventNameSubscriberEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="Subscriber">
        <Property Name="Subscribe" />
      </Object>
    </Item>
    <Item>SecondEventName</Item>
  </Items>
</Condition>

А еще можно использовать get-проперти Parameter - получать значение конкретного параметра и применять его в условии.

Создадим Execution, который по условию срабатывания события будет вызывать команду на обновление:

TemplateStart.xml
<Execution>
  <ConditionExpression>
    <Condition Name="NumberOfOrdersSubscriberChangedCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="OrderDataConnectionRefreshCommand" />
  </Commands>
</Execution>

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

Так как теперь стартовая форма получает сообщения от сервера о новых данных и автоматически обновляет OrderPrimaryGetDataConnection, то из <Execution> при добавлении или изменении заказа нужно убрать вызовы команды OrderDataConnectionRefreshCommand, чтобы не было повторного обращения к серверу. Но оставить команду на выделение измененной строки в таблице.

Итоги

В этом уроке мы познакомились с механизмами автоматического обновления данных, получаемых от сервера. У каждого способа есть свои минусы и области применения.

Тэг <UpdateInterval> у PrimaryGetDataConnection позволяет автоматически обновлять данные, и не требует создания дополнительных команд, условий и Execution. Но нужно следить за правами доступа. И нельзя контролировать время запуска и количество циклов выполнения обновления.

С помощью объекта Timer можно выполнять различные последовательности команд с контролируемыми временем начала и количеством циклов выполнения. Но, если использовать его для обновления данных на форме - будут происходить лишние обращения к серверу.

Способ с подпиской формы на событие, которое рассылает сервер всем клиентским частям при выполнении запроса на изменение данных, является самым подходящим, т.к. исключает лишние запросы к серверу. Его так же можно использовать и для выполнения других команд.

Ответы

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

Last updated