logo

Рекомендации по работе во втором рантайме

Общие рекомендации

  1. Формы процессных задач — не перегружайте информацией формы задач. Для этого делите одну задачу на несколько, последовательно выполняемых в ходе процесса.
  2. Одна вкладка для работы — старайтесь настроить работу пользователя преимущественно на одной вкладке браузера. Открытие новых вкладок запускает заново загрузку данных для этой же страницы.
  3. Панель, Вкладки, Выдвижная панель, Модальное окно — в настройках этих и пользовательских компонентов выбирайте тип загрузки При отображении или Ленивая:
  • Ленивая — предпочтительный вариант;
  • При отображении — нужно указывать, чтобы загрузка срабатывала при первом изменении значения настройки Скрыть.
  1. Входные параметры — при использовании на форме компонентов входные параметры следует передавать простыми типами данных или одним объектом через структуру обмена данных вместо множества параметров.

Если на сервер нужно отправить данные, а подходящей модели нет

Не нужно создавать новый справочник. Достаточно создать структуру обмена данные в разделе Интерфейсы. Эти объекты не хранятся в базе, они служат только для транспортировки данных между компонентами, сервером и клиентом.

Контекст процесса

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

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

Клиентские сценарии

Не следует на сервер отправлять context, в котором множество свойств. При этом все данные сериализуются и десериализуются, а также ещё учитываются метаданные. Это трудоёмкий процесс, который занимает очень много времени. Нужно использовать клиентские сценарии с использованием свойства типа Действие или использовать обращение к серверному методу через Server. Например:

await Server.Change(text);

Свойство типа «Действие»

При создании такого свойства в поле Тип выберите Действие, укажите входные и выходные параметры (можно указать структуру обмена данными), в опции Значение по умолчанию создайте серверный метод.

Такое свойство в клиентском сценарии доступно в контексте. Например:

await Context.Change(text);

Заполнение формы или изменение элементов формы

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

Решение:

  1. Создать в контексте свойство с типом Действие, например OnChangeProperty.
  2. Указать для созданного свойства входные/выходные параметры.
  3. В настройке свойства При изменении значения создать клиентский метод (!) и внутри него вызвать OnChangeProperty. Пример сценария:
///
public async Task OnChange(IChangeEvent<string> e)
{
	if (e != null && e.Value != null) {
		await Context.OnChangeProperty(e.Value);
	}
}
///

Сценарии при загрузке формы или блока

Не рекомендуем использовать серверные сценарии при загрузке формы или блока.

Решение:

  1. Создать в контексте свойство с типом Действие, например OnLoadForm.
  2. Открыть клиентский сценарий:
using EleWise.ELMA.Core.Controllers;

partial class ViewModelController : IOnLoadAsync
{
	public async Task OnLoadAsync() {
		var result = await Context.OnLoadForm();
		// из result можно заполнить контекст
		// если возвращаемым типом указать структуру обмена данных/объект со свойством строка
		// Context.MyProp = result.Text;
		
		// еще в настройка OnLoadForm можно указать входные параметры
	}
}

Примечание: помимо IOnLoadAsync для контроллера существуют IOnInit, IOnValidate.

Рекомендации по применению DTO

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

В случае, если была загружена и сразу сохранена в БД сущность с зависимыми данными, то риск потери этих данных минимален, т. к. загрузка и сохранение выполняются в одной транзакции.
В зоне риска потери данных находится случай, когда сущность загружается на клиента, а через некоторое время — обратно на сервер. Например, если объект содержит свойство DTO (оно может быть любой вложенности, в том числе include), то он будет пересохранён.

Рассмотрим пример со структурой обмена данными (DTO), которая содержит свойство Sale типа Сделка.

  1. Загружаем DTO с сервера. Вместе со ссылкой загрузится весь объект Сделка.
  2. Позже передаём DTO в серверный сценарий. При этом DTO передаётся со всеми связанными объектами.
  3. Выполняем в серверном сценарии сохранение сущности (любой, даже и не связанной со сделкой) в базу данных. При этом сущность Сделка тоже сохранится.

Если за время, прошедшее с загрузки сущности из БД до её сохранения, сущность была изменена в другом месте системы, то изменения перезапишутся на текущие.

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

Варианты решения

Выйти из этой ситуации можно тремя способами:

  1. При выполнении серверного сценария, если с клиента загружается объект, НЕ выполнять сохранения в БД в этом сценарии.
  2. Если в сценарии требуется выполнить сохранение в БД, для передачи данных в сценарий использовать отдельную DTO, в которой использовать только простые типы, а ссылки на сущности передавать как число и загружать объект уже в самом сценарии из базы данных.
  3. Создать новую сессию, например, запустить новую Task.Run() с ожиданием, и в ней загружать и сохранять сущности, изменения которых нужно записать в БД:
    await Task.Run(() =>{
        using (new CallContextSessionOwner())
        {
            // тут загружаем из базы нужные сущности, записываем в них нужные изменения и сохраняем
        }
    });