WT. Практика (Desktop)
Платформа WTПрактикаСинтаксисБаза знаний
  • Приветствие
  • Основной
    • Урок 1. Форма списка и добавление записей
    • Урок 2. Редактирование таблицы
    • Урок 3. Выпадающий список
    • Урок 4. Паттерн onClose
    • Урок 5. Удаление связанных данных
    • Урок 6. Главная форма
    • Урок 7. Фильтры
    • Урок 8. Редактирование выпадающего списка
    • Урок 9. Список категорий
    • Урок 10. Паттерн Add/Edit
    • Урок 11. Экспорт данных в документ
    • Урок 12. Дерево в таблице
    • Урок 13. Самостоятельная
    • Урок 14. Постраничный просмотр
    • Дополнительно
      • Array
      • ArrayGetDataConnection
      • ConvertDataConnection
  • Загрузка данных
    • Урок 15. Режимы загрузки данных
    • Урок 16. Режим блокировки форм (Lock)
  • Многопользовательский режим
    • Урок 17. Аутентификация пользователей в программе
    • Урок 18. Права доступа
    • Урок 19. Динамические права доступа
    • Урок 20. Пользовательские настройки
    • Урок 21. Автоматическое обновление данных
  • Кастомизация
    • Урок 22. Создание кастомных команд для форм
    • Урок 23. Создание кастомных команд для серверной части
    • Урок 24. Планировщик задач
  • Продвинутый уровень
    • Урок 25. Создание API-запросов
    • Урок 26. Работа с JSON на форме
    • Урок 27. Разделение формы на несколько файлов
Powered by GitBook
On this page
  • Форма настроек
  • Вкладки
  • Элемент выбора режима
  • Отладка приложений
  • Настройки
  • WorkflowVisualizer
  • Режим загрузки данных (LoadMode)
  • Режим 0 - последовательный
  • Режим 1 - пакетный
  • Режим 2 - параллельный
  • Ручное управление загрузкой данных
  • DependOn
  • ManualLoad
  • DataConnectionRefreshCommand
  • Параллельное выполнение запросов
  • Get-проперти LoadMode
  • Итого
  • Ответы
  1. Загрузка данных

Урок 15. Режимы загрузки данных

Last updated 1 year ago

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

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

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

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

Платформа Workflow Technology поддерживает три режима загрузки данных:

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

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

Обеспечивает увеличенную скорость загрузки форм, однако при этом требует максимальное количество ресурсов со стороны сервера. Режим по умолчанию.

Эти режимы описаны в таблице public.load_mode, а в поле selected этой таблицы можно выбирать текущий режим.

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

Платформа при загрузке формы строит дерево зависимостей для запросов в DataConnection (в одном DataConnection может быть несколько запросов). При этом учитываются все связи между ними (прямые и косвенные через команды и объекты). На основе этого дерева строится порядок выполнения запросов. Первыми всегда будут выполняться те запросы, которые не зависят от результата выполнения других запросов.

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

Форма настроек

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

Очень удобно, когда пользователь имеет возможность самостоятельно выбирать режим загрузки данных и искать тот, который наилучшим образом ускорит работу. Давайте реализуем такую возможность, добавив на форму "Настройки" поле "Режим загрузки данных". При этом отделим настройки почтового агента от настроек загрузки данных, вынеся их на отдельную вкладку.

В результате должна получиться форма вида:

Вкладки

В платформе нет отдельного объекта "Вкладки", вместо этого используются обычные кнопки (Button), которые изменяют видимость панелей (Panel).

Перейдем в файл TemplateSettings.xml.

Добавим цвет, которым будем подсвечивать вкладку при наведении на нее курсор мыши:

TemplateSettings.xml
<Color Name="TabMouseOverBackColor" Red="201" Green="219" Blue="241" Alpha="255" />
TemplateSettings.xml
<MyObject Name="ActiveTabVariable" Type="Variable" Assembly="SimpleControls" ChangeForm="False">
  <Value>General</Value>
</MyObject>

Так как на форме настроек у нас будет две вкладки: основные настройки (General) и настройки почтового агента (Email), то создадим два условия:

TemplateSettings.xml
<Condition Name="GeneralTabActiveEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="ActiveTabVariable" />
    </Item>
    <Item>General</Item>
  </Items>
</Condition>

<Condition Name="EmailTabActiveEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="ActiveTabVariable" />
    </Item>
    <Item>Email</Item>
  </Items>
</Condition>
TemplateSettings.xml
<Command Name="ActiveTabValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="ActiveTabVariable">
    <Input />
  </Object>
</Command>

Теперь можем создать сами кнопки, которые будут выступать в качестве вкладок. Добавим описание кнопок в панель HeadPanel:

TemplateSettings.xml
<MyObject Name="EmailTabButton" Type="Button" Assembly="BaseControls">
  <Bottom>
    <Object Name="HeadPanel">
      <Property Name="Height" />
    </Object>
  </Bottom>
  <Right>
    <Formula>
      <Minus DataType="IntegerDataType">
        <Item>
          <Object Name="HeadPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>10</Item>
      </Minus>
    </Formula>
  </Right>
  <Height>30</Height>
  <Width>100</Width>
  <TextAlign>MiddleCenter</TextAlign>
  <Text>Email</Text>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>0</FlatBorderSize>
  <BackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="EmailTabActiveEqualCondition" />
        </When>
        <Then>ThemeBackgroundColor</Then>
      </Case>
      <Case>HeadBackgroundColor</Case>
    </Switch>
  </BackColor>
  <FlatMouseOverBackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="EmailTabActiveEqualCondition" />
        </When>
        <Then>ThemeBackgroundColor</Then>
      </Case>
      <Case>TabMouseOverBackColor</Case>
    </Switch>
  </FlatMouseOverBackColor>
  <FlatMouseDownBackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="EmailTabActiveEqualCondition" />
        </When>
        <Then>ThemeBackgroundColor</Then>
      </Case>
      <Case>TabMouseOverBackColor</Case>
    </Switch>
  </FlatMouseDownBackColor>
  <ForeColor>HeadForeColor</ForeColor>
  <FontStyle>ButtonFontStyle</FontStyle>
  <Commands>
    <Command Name="ActiveTabValueSetCommand">Email</Command>
  </Commands>
</MyObject>

<MyObject Name="GeneralTabButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="EmailTabButton">
      <Property Name="Top" />
    </Object>
  </Top>
  <Right>
    <Object Name="EmailTabButton">
      <Property Name="Left" />
    </Object>
  </Right>
  <Height>30</Height>
  <Width>100</Width>
  <TextAlign>MiddleCenter</TextAlign>
  <Text>Основное</Text>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>0</FlatBorderSize>
  <BackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="GeneralTabActiveEqualCondition" />
        </When>
        <Then>ThemeBackgroundColor</Then>
      </Case>
      <Case>HeadBackgroundColor</Case>
    </Switch>
  </BackColor>
  <FlatMouseOverBackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="GeneralTabActiveEqualCondition" />
        </When>
        <Then>ThemeBackgroundColor</Then>
      </Case>
      <Case>TabMouseOverBackColor</Case>
    </Switch>
  </FlatMouseOverBackColor>
  <FlatMouseDownBackColor>
    <Switch>
      <Case>
        <When>
          <Condition Name="GeneralTabActiveEqualCondition" />
        </When>
        <Then>ThemeBackgroundColor</Then>
      </Case>
      <Case>TabMouseOverBackColor</Case>
    </Switch>
  </FlatMouseDownBackColor>
  <ForeColor>HeadForeColor</ForeColor>
  <FontStyle>ButtonFontStyle</FontStyle>
  <Commands>
    <Command Name="ActiveTabValueSetCommand">General</Command>
  </Commands>
</MyObject>

Сократим ширину объекта HeadLabel, чтобы он не перекрывал кнопки.

Переименуем ContentPanel в EmailPanel и создадим GeneralPanel. Добавим в панели условия видимости с соответствующими Condition.

Запустите приложение и проверьте загрузку формы настроек и отображение вкладок:

Элемент выбора режима

Перейдем в серверный xml-файл и добавим запрос:

Template.xml
<SqlQuery Name="LoadModeSelectSqlQuery">
  <Text>
    SELECT
      load_mode_id AS "LoadModeId",
      string_value(title, {PublicUserId}) AS "Title",
      string_value(description, {PublicUserId}) AS "Description",
      selected AS "Selected"
    FROM public.load_mode;
  </Text>
</SqlQuery>

В этом запросе применяется функция public.string_value(character varying, integer), которая предназначена для получения строкового ресурса из таблицы public.strings. Для этого функция использует ключ ресурса (strings_id) и системную переменную {PublicUserId} (идентификатор пользователя). По идентификатору пользователя из таблицы public.user получаем идентификатор языка (language_id).

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

Добавим новый запрос в SettingsViewSqlQueryPermission.

Скорректируем запрос SettingsUpdateSqlQuery, добавив сохранение выбранного режима загрузки данных:

Template.xml
<SqlQuery Name="SettingsUpdateSqlQuery">
  <Text>
    UPDATE template.settings
    SET
      smtp_server = {SmtpServerAddress},
      smtp_port = {SmtpServerPort},
      ssl = {SSL},
      email_address = {Email},
      email_password = {EmailPassword};
          
    UPDATE load_mode
    SET
      selected = (load_mode_id = {LoadModeId});
  </Text>
</SqlQuery>

Вернемся в файл формы настроек (TemplateSettings.xml) и создадим загружающее соединение с данными, которое будем использовать в тэге <ValueList> объекта LoadModeComboBox:

TemplateSettings.xml
<DataConnection Name="LoadModePrimaryGetDataConnection" Type="PrimaryGetDataConnection" Assembly="DataConnections">
  <SqlQuery Name="LoadModeSelectSqlQuery" Type="Select">
    <Workflow Name="Template" />
    <Fields>
      <Field Name="LoadModeId" />
      <Field Name="Title" />
      <Field Name="Description" />
      <Field Name="Selected" />
    </Fields>
  </SqlQuery>
</DataConnection>

Так же создадим вторичное соединение с данными, в котором получим запись активного режима загрузки данных и будем передавать его идентификатор в тэг <Value> объекта LoadModeComboBox:

TemplateSettings.xml
<DataConnection Name="LoadModeSelectedSecondaryGetDataConnection" Type="SecondaryGetDataConnection" Assembly="DataConnections">
  <SourceDataConnection Name="LoadModePrimaryGetDataConnection" />
  <Filter>
    <Field NativeName="Selected" />
    <Value>True</Value>
    <DataType Type="BooleanDataType" />
  </Filter>
</DataConnection>

Создайте выпадающий список "Режим загрузки данных" (LoadModeComboBox), для которого запретите ввод пустого значения (NullValue = False).

Добавим Label с описанием режима, чтобы пользователю была доступна информация об особенностях выбранного режима загрузки данных.

Для начала добавим цвет для надписи:

TemplateSettings.xml
<Color Name="HintForeColor" Red="160" Green="160" Blue="160" Alpha="255" />

А также добавим стиль шрифта:

TemplateSettings.xml
<FontStyle Name="HintFontStyle" Font="Segoe UI" Size="8" />

Добавим вторичное соединение с данными, в котором будем фильтровать записи относительно выбранного в ComboBox режима:

TemplateSettings.xml
<DataConnection Name="LoadModeDescriptionSecondaryGetDataConnection" Type="SecondaryGetDataConnection" Assembly="DataConnections">
  <SourceDataConnection Name="LoadModePrimaryGetDataConnection" />
  <Filter>
    <Field NativeName="LoadModeId" />
    <Value>
      <Object Name="LoadModeComboBox" />
    </Value>
    <DataType Type="IntegerDataType" />
  </Filter>
</DataConnection>

Создадим LoadModeDescriptionLabel:

TemplateSettings.xml
<MyObject Name="LoadModeDescriptionLabel" Type="Label" Assembly="BaseControls" ChangeForm="False">
  <Top>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="LoadModeComboBox">
            <Property Name="Bottom" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Top>
  <Left>
    <Object Name="LoadModeComboBox">
      <Property Name="Left" />
    </Object>
  </Left>
  <Height>40</Height>
  <Width>
    <Object Name="LoadModeComboBox">
      <Property Name="Width" />
    </Object>
  </Width>
  <TextAlign>TopLeft</TextAlign>
  <FontStyle>HintFontStyle</FontStyle>
  <ForeColor>HintForeColor</ForeColor>
  <Text>
    <DataConnection SourceDataConnection="LoadModeDescriptionSecondaryGetDataConnection">
      <Fields>
        <Field Name="Description" />
      </Fields>
    </DataConnection>
  </Text>
</MyObject>

Откроем форму настроек и проверим расположение элементов:

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

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

<Condition Name="LoadModeIsChangedNotEqualCondition" Type="NotEqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <DataConnection SourceDataConnection="LoadModeSelectedSecondaryGetDataConnection">
        <Fields>
          <Field Name="LoadModeId" />
        </Fields>
      </DataConnection>
    </Item>
    <Item>
      <Object Name="LoadModeComboBox" />
    </Item>
  </Items>
</Condition>

В условии LoadModeIsChangedNotEqualCondition можно проверять свойство ValueChanged объекта LoadModeComboBox, но сравнивая два значения, мы будем отлавливать точные изменения значения в базе данных без лишних срабатываний.

Создадим команду для отображения уведомления:

<Command Name="LoadModeChangedMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
  <Condition Name="LoadModeIsChangedNotEqualCondition" />
  <Caption>Изменение режима загрузки данных</Caption>
  <Text>ВНИМАНИЕ!\r\rЧтобы изменение режима загрузки данных вступили в силу, необходимо перезапустить сервер и переоткрыть приложение.</Text>
  <Icon Type="Information" />
  <Buttons Type="Ok" />
</Command>

Добавьте вызов LoadModeChangedMessageBoxCommand в команду SaveSequentialCommand сразу после вызова команды SettingsSaveCommand.

Запустите приложение и проверьте сохранение данных на форме.

Теперь можем познакомиться с утилитой WorkflowVisualizer.

Отладка приложений

Текущая версия редактора Workflow XML Editor не позволяет протестировать загрузку форм и выполнение команд. Для таких целей существует утилита WorkflowVisualizer.

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

Настройки

Утилита читает файлы логов от приложения и на их основе строит диаграммы. Чтобы приложение сохраняло логи, необходимо включить режим отладки (DebugMode) и указать каталог (DebugPath), в который будут писаться логи. Для сервера и форм режим отладки включается отдельно.

Чтобы серверная часть писала логи, необходимо в файл appsettings.json добавить поля:

"DebugMode":"true",
"DebugPath":"D:\\Template\\DebugDC"

Для включения режима на клиентской части в файл WorkflowForms.dll.config добавляются настройки:

<setting name="DebugMode" serializeAs="String">
  <value>True</value>
</setting>
<setting name="DebugPath" serializeAs="String">
  <value>D:\Template\DebugDC</value>
</setting>

Перезапустите сервер и приложение, проверьте наличие папки с файлами логов.

WorkflowVisualizer

Запустим приложение WorkflowVisualizer.

В поле DebugFolder укажем путь до нашего каталога и нажмем на кнопку "Загрузить". WorkflowVisualizer подхватит файлы логов и построит диаграмму загрузки PrimaryGetDataConnection:

Полнота диаграммы настраивается галочками:

  • SqlQuery - время обновления PrimaryGetDataConnection и конкретного SqlQuery на форме;

  • SqlQueryEngine - время исполнения запрос на контроллере сервера;

  • SqlQueryReader - время обращения сервера к базе данных;

  • PrimaryGetDataConnection - время обновления PrimaryGetDataConnection на форме. Обычно тут не бывает элементов, так как они слиты с SqlQuery;

  • SecondaryDataConnection - время обновления SecondaryDataConnection;

  • ArrayDataConnection - время обновления ArrayDataConnection;

  • ConvertDataConnection - время обновления ConvertDataConnection;

  • Condition - изменение Condition;

  • Execution - сработавшие Execution на форме;

  • Command - сработавшие команды;

  • OnSuccess - время, связанное с обновлением всех подписанных на DataConnection объектов;

  • Unknown - все остальные.

Элементы SqlQueryEngine и SqlQueryReader будут отображаться только в том случае, если в серверном файле конфигурации включен режим отладки DebugMode.

Давайте поставим галочки SqlQuery, SqlQueryEngine и SqlQueryReader и посмотрим, как изменится диаграмма:

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

Режим загрузки данных (LoadMode)

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

Режим 0 - последовательный

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

На диаграмме видно, что ClientPrimaryGetDataConnection, зависящий от поля ClientId соединения OrderPrimaryGetDataConnection, загрузился последним. А остальные загрузились в порядке описания в файле.

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

Искусственно утяжелим запрос OrderByIdSelectSqlQuery, добавив функцию pg_sleep() с задержкой на 2 секунды.

<SqlQuery Name="OrderByIdSelectSqlQuery">
  <Text>
    SELECT
      O.order_id AS "OrderId",
      O.client_id AS "ClientId",
      O.order_number AS "OrderNumber",
      O.order_date AS "OrderDate",
      O.description AS "Description",
      
      pg_sleep(2) -- "утяжеляем" запрос на 2 секунды
    FROM
      template.order O
    WHERE
      O.order_id = {OrderId};
  </Text>
</SqlQuery>

Открыв повторно карточку заказа, на диаграмме увидим картину:

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

В такой ситуации ждать результата OrderPrimaryGetDataConnection необходимо только для ClientPrimaryGetDataConnection, так как он зависит от значения в поле ClientId. А остальные DataConnection можно запустить параллельно, чтобы не терять время в пустом ожидании.

Режим 1 - пакетный

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

На диаграмме видим, что в первый пакет попало шесть запросов. Запрос ClientShortSelectSqlQuery отправился на сервер во втором пакете после того, как выполнился самый тяжелый запрос OrderByIdSelectSqlQuery первого пакета.

Для OrderPositionPrimaryGetDataConnection и OrderPaymentPrimaryGetDataConnection укажем зависимость от OrderPrimaryGetDataConnection и посмотрим на диаграмму:

В первом пакете осталось четыре запроса, а во второй пакет к ClientShortSelectSqlQuery попали OrderPositionByOrderIdSelectSqlQuery и OrderPaymentByOrderIdSelectSqlQuery.

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

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

Режим 2 - параллельный

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

Для OrderPositionPrimaryGetDataConnection поменяем зависимость на MaterialPrimaryGetDataConnection. А с OrderPaymentPrimaryGetDataConnection удалим зависимость. Искусственно утяжелим запрос MaterialSimpleSelectSqlQuery.

В WorkflowVisualizer увидим картину:

После того как на сервер ушли все запросы первой волны, ClientPrimaryGetDataConnection не стал дожидаться завершения MaterialPrimaryGetDataConnection, а сразу же ушел на сервер, как только был готов результат OrderPrimaryGetDataConnection. В то время как OrderPositionPrimaryGetDataConnection дожидался завершения запроса, от которого зависит и отправился в третьей волне.

Ручное управление загрузкой данных

Есть разные возможности, которые позволяют разработчику контролировать порядок загрузки данных при старте формы помимо выбора режима LoadMode. С одной такой возможностью мы уже познакомились - тэг <DependOn>.

DependOn

ManualLoad

DataConnectionRefreshCommand

В команде DataConnectionRefreshCommand все DataConnection можем разбивать на пакеты, используя атрибуты Packet (разрешает загрузку DC в пакете) и PacketGroup (текстовая метка пакета):

<Command Name="AllDataConnectionRefreshCommand" Type="DataConnectionRefreshCommand" Assembly="Commands">
  <DataConnections>
    <!-- Первый пакет -->
    <DataConnection Name="1PrimaryGetDataConnection" Packet="True" PacketGroup="0"/>
    <DataConnection Name="2PrimaryGetDataConnection" Packet="True" PacketGroup="0"/>
    <!-- Второй пакет -->
    <DataConnection Name="3PrimaryGetDataConnection" Packet="True" PacketGroup="1"/>
    <DataConnection Name="4PrimaryGetDataConnection" Packet="True" PacketGroup="1"/>
  </DataConnections>
</Command>

При разбиении DataConnection на пакеты в команде типа DataConnectionRefreshCommand необходимо придерживаться следующих правил:

  • Имена пакетов, указанные в атрибуте PacketGroup, являются текстовой меткой пакета и не определяют порядок выполнения пакетов. Порядок выполнения пакетов задается порядком упоминания метки пакета в команде;

  • DataConnection из одного пакета лучше описывать друг за другом, чтобы не создавать путаницы;

  • В первый, в порядке объявления, пакет следует указывать запросы, которые не зависят от других запросов;

  • В следующий, в порядке объявления, пакет должны попадать запросы, которые зависят от запросов из предыдущего пакета;

  • Если в команде не указано явное разбиение на пакеты, то считается, что все запросы находятся в одном пакете.

В качестве эксперимента на форме карточки заказа переведем все PrimaryGetDataConnection в режим ручной загрузки (ManualLoad = True) и создадим команду, в которой все DataConnection разобьем на четыре пакета:

TemplateOrderEdit.xml
<Command Name="AllDataConnectionRefreshCommand" Type="DataConnectionRefreshCommand" Assembly="Commands">
  <DataConnections>
    <!-- Пакет 0 -->
    <DataConnection Name="SettingsPrimaryGetDataConnection" Packet="True" PacketGroup="0" />
    <DataConnection Name="AccountPrimaryGetDataConnection" Packet="True" PacketGroup="0" />
    <DataConnection Name="MaterialPrimaryGetDataConnection" Packet="True" PacketGroup="0" />

    <!-- Пакет 1 -->
    <DataConnection Name="OrderPrimaryGetDataConnection" Packet="True" PacketGroup="1" />

    <!-- Пакет 2 -->
    <DataConnection Name="OrderPositionPrimaryGetDataConnection" Packet="True" PacketGroup="2" />
    <DataConnection Name="OrderPaymentPrimaryGetDataConnection" Packet="True" PacketGroup="2" />

    <!-- Пакет 3 -->
    <DataConnection Name="ClientPrimaryGetDataConnection" Packet="True" PacketGroup="3" />
  </DataConnections>
</Command>

Вызовем эту команду в Execution, предварительно описав системное условие FormLoadedCondition:

TemplateOrderEdit.xml
<Execution>
  <ConditionExpression>
    <Condition Name="FormLoadedCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="AllDataConnectionRefreshCommand" />
  </Commands>
</Execution>

При загрузке формы увидим примерную диаграмму:

Здесь можем наблюдать повторное обновление ClientPrimaryGetDataConnection. Первое обновление произошло автоматически из-за изменения параметра ClientId, которое было вызвано обновлением OrderPrimaryGetDataConnection.

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

Давайте добавим такой атрибут для параметра ClientId в ClientPrimaryGetDataConnection и проверим диаграмму загрузки:

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

Обновление объектов на форме

Но теперь возникает другая проблема на форме: ClientComboBox теряет свое значение.

Причина этого в том, что OrderPrimaryGetDataConnection обновляется раньше, чем ClientPrimaryGetDataConnection, который является источником данных для выпадающего списка. Таким образом, мы пытаемся подставить в ClientComboBox значение (Value), которого нет в списке (ValueList). И самого списка еще нет на форме. ClientComboBox забывает это значение.

<AllowOutOfList Value="True" />
<Change User="True" Source="True" ValueSet="True" />

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

Поэтому указывайте тэг <Change> с нужными значениями атрибутов:

<Change User="True" Source="False" ValueSet="True" />

Параллельное выполнение запросов

По умолчанию команда DataConnectionRefreshCommand отправляет все загружающие соединения с данными в рамках одного PacketGroup на сервер одним запросом. Форма дожидается результата обновления всех запросов из пакета и после этого рассылает событие своим объектам для обновления значений.

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

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

И при включении параллельной отправки запросов мы получим следующую картину:

Get-проперти LoadMode

<Condition Name="LoadModeNotEqualOneCondition" Type="NotEqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Form>
        <Property Name="LoadMode" />
      </Form>
    </Item>
    <Item>1</Item>
  </Items>
  <DataType Type="IntegerDataType" />
</Condition>
TemplateOrderEdit.xml
<Command Name="AllDataConnectionRefreshCommand" Type="DataConnectionRefreshCommand" Assembly="Commands">
  <Parallel>
    <Condition Name="LoadModeNotEqualOneCondition" />
  </Parallel>
  <DataConnections>
    <!-- Пакет 0 -->
    <DataConnection Name="SettingsPrimaryGetDataConnection" Packet="True" PacketGroup="0" />
    <DataConnection Name="AccountPrimaryGetDataConnection" Packet="True" PacketGroup="0" />
    <DataConnection Name="MaterialPrimaryGetDataConnection" Packet="True" PacketGroup="0" />

    <!-- Пакет 1 -->
    <DataConnection Name="OrderPrimaryGetDataConnection" Packet="True" PacketGroup="1" />

    <!-- Пакет 2 -->
    <DataConnection Name="OrderPositionPrimaryGetDataConnection" Packet="True" PacketGroup="2" />
    <DataConnection Name="OrderPaymentPrimaryGetDataConnection" Packet="True" PacketGroup="2" />

    <!-- Пакет 3 -->
    <DataConnection Name="ClientPrimaryGetDataConnection" Packet="True" PacketGroup="3" />
  </DataConnections>
</Command>

Итого

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

Ответы

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

Создадим переменную , в которой будем хранить значение активной вкладки:

Создадим команду для изменения значения активной вкладки:

Зависимостью можно управлять через тэг , указав в нем список DataConnection, которые должны быть выполнены раньше текущего.

Управление зависимостью через тэг в описании PrimaryGetDataConnection - так мы можем контролировать построение дерева зависимостей в момент загрузки формы.

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

Чтобы такого не происходило, используйте в ComboBox тэг :

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

Для таких случаев команда DataConnectionRefreshCommand имеет вложенный тэг . Если его значение True, то все запросы в рамках одного PacketGroup отправляет на сервер параллельно. Форма не дожидается результатов всех запросов из пакета, а рассылает событие объектам по мере готовности данных.

У формы есть get-проперти , которое возвращает идентификатор выбранного режима загрузки данных. Значение этого get-проперти можно сравнивать со значением 1 - выбран ли параллельный режим загрузки:

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

Variable
ValueSetCommand
<DependOn>
<DependOn>
<ManualLoad>
DataConnectionRefreshCommand
Execution
FormLoadedCondition
<AllowOutOfList>
<Change>
<Parallel>
LoadMode
<Parallel>
Режим 0 - последовательный
Режим 1 - пакетный
Режим 2 - параллельный
Разворачивание проекта
<ManualLoad>
DataConnectionRefreshCommand
Режим загрузки данных (LoadMode)
ссылке
627KB
WorkflowVisualizer.zip
archive
61MB
WorkflowVisualizer (portable).zip
archive
530KB
lesson15-answer.zip
archive
Ответы