Рекомендации по работе во втором рантайме
Общие рекомендации
- Формы процессных задач — не перегружайте информацией формы задач. Для этого делите одну задачу на несколько, последовательно выполняемых в ходе процесса.
- Одна вкладка для работы — старайтесь настроить работу пользователя преимущественно на одной вкладке браузера. Открытие новых вкладок запускает заново загрузку данных для этой же страницы.
- Панель, Вкладки, Выдвижная панель, Модальное окно — в настройках этих и пользовательских компонентов выбирайте тип загрузки При отображении или Ленивая:
- Ленивая — предпочтительный вариант;
- При отображении — нужно указывать, чтобы загрузка срабатывала при первом изменении значения настройки Скрыть.
- Входные параметры — при использовании на форме компонентов входные параметры следует передавать простыми типами данных или одним объектом через структуру обмена данных вместо множества параметров.
Если на сервер нужно отправить данные, а подходящей модели нет
Не нужно создавать новый справочник. Достаточно создать структуру обмена данные в разделе Интерфейсы. Эти объекты не хранятся в базе, они служат только для транспортировки данных между компонентами, сервером и клиентом.
Контекст процесса
Различайте клиентский и серверный контекст. Так, для отображения на формах (разные скрытия, обозначения цветом и другие состояния) используйте клиентский контекст. А настройки, которые применяются в ходе всего процесса, должны быть в серверном контексте.
Чтобы создать свойство для клиентского контекста, откройте форму процесса или компонента, затем создайте свойство и включите настройку Только для веб-части.
Клиентские сценарии
Не следует на сервер отправлять context, в котором множество свойств. При этом все данные сериализуются и десериализуются, а также ещё учитываются метаданные. Это трудоёмкий процесс, который занимает очень много времени. Нужно использовать клиентские сценарии с использованием свойства типа Действие или использовать обращение к серверному методу через Server. Например:
await Server.Change(text);
Свойство типа «Действие»
При создании такого свойства в поле Тип выберите Действие, укажите входные и выходные параметры (можно указать структуру обмена данными), в опции Значение по умолчанию создайте серверный метод.
Такое свойство в клиентском сценарии доступно в контексте. Например:
await Context.Change(text);
Заполнение формы или изменение элементов формы
Не рекомендуем создавать серверные методы напрямую в настройках. Например, при изменении свойства, загрузке формы и т. п.
Решение:
- Создать в контексте свойство с типом Действие, например OnChangeProperty.
- Указать для созданного свойства входные/выходные параметры.
- В настройке свойства При изменении значения создать клиентский метод (!) и внутри него вызвать OnChangeProperty. Пример сценария:
///
public async Task OnChange(IChangeEvent<string> e)
{
if (e != null && e.Value != null) {
await Context.OnChangeProperty(e.Value);
}
}
///
Сценарии при загрузке формы или блока
Не рекомендуем использовать серверные сценарии при загрузке формы или блока.
Решение:
- Создать в контексте свойство с типом Действие, например OnLoadForm.
- Открыть клиентский сценарий:
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 типа Сделка.
- Загружаем DTO с сервера. Вместе со ссылкой загрузится весь объект Сделка.
- Позже передаём DTO в серверный сценарий. При этом DTO передаётся со всеми связанными объектами.
- Выполняем в серверном сценарии сохранение сущности (любой, даже и не связанной со сделкой) в базу данных. При этом сущность Сделка тоже сохранится.
Если за время, прошедшее с загрузки сущности из БД до её сохранения, сущность была изменена в другом месте системы, то изменения перезапишутся на текущие.
Также, если в DTO есть сущность и в ней изменили свойство сущности, то изменения перезапишутся на текущие, при использовании любого Save в методе.
Варианты решения
Выйти из этой ситуации можно тремя способами:
- При выполнении серверного сценария, если с клиента загружается объект, НЕ выполнять сохранения в БД в этом сценарии.
- Если в сценарии требуется выполнить сохранение в БД, для передачи данных в сценарий использовать отдельную DTO, в которой использовать только простые типы, а ссылки на сущности передавать как число и загружать объект уже в самом сценарии из базы данных.
- Создать новую сессию, например, запустить новую Task.Run() с ожиданием, и в ней загружать и сохранять сущности, изменения которых нужно записать в БД:
await Task.Run(() =>{ using (new CallContextSessionOwner()) { // тут загружаем из базы нужные сущности, записываем в них нужные изменения и сохраняем } });