При разработке функционала на управляемых формах в 1С:Предприятии 8.3 часто возникает задача: получить большой массив данных на сервере (например, из внешней системы, базы данных или тяжелого запроса), сохранить его в виде объекта ТаблицаЗначений и далее многократно обращаться к этим данным в контексте сервера при действиях пользователя, не передавая всю тяжелую таблицу на тонкий клиент.
Разработчики часто сталкиваются со следующими проблемами при попытке решить эту задачу классическими путями:
&НаСервере не помогает — сервер 1С работает по принципу отсутствия состояния (stateless), и между отдельными серверными вызовами значения переменных модуля сбрасываются в Неопределено.ТаблицаЗначений доступен в качестве реквизита формы, однако данные реквизитов формы сериализуются и при контекстных вызовах передаются между клиентом и сервером по сети, что создает существенные накладные расходы при больших объемах информации.ПоместитьВоВременноеХранилище(ТЗ) без указания идентификатора формы приводит к тому, что данные удаляются сразу после завершения текущего вызова сервера.Разберем подробно, почему так происходит, и рассмотрим правильные архитектурные способы хранения промежуточных таблиц значений на сервере.
Проанализируем механику работы временного хранилища платформы 1С. Согласно синтаксис-помощнику, если при вызове функции ПоместитьВоВременноеХранилище не передан второй параметр, помещенное значение удаляется автоматически после очередного серверного вызова из формы или общего модуля.
Чтобы данные жили столько же, сколько открыта форма, во второй параметр метода необходимо передать системное свойство формы — УникальныйИдентификатор. В этом случае время жизни данных синхронизируется с жизненным циклом конкретного экземпляра управляемой формы, а при ее закрытии платформа автоматически удалит данные из хранилища.
Рассмотрим по шагам реализацию этого механизма:
АдресХранилищаТЗ для хранения адреса во временном хранилище.УникальныйИдентификатор формы.ПолучитьИзВременногоХранилища.Посмотрим на пример кода в модуле формы:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Создаем и заполняем таблицу значений на сервере
ТЗДанные = Новый ТаблицаЗначений;
ТЗДанные.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Число"));
ТЗДанные.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(150)));
ТЗДанные.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число",,,, Новый КвалификаторыЧисла(15, 3)));
// Заполняем таблицу исходными данными
Для Индекс = 1 По 1000 Цикл
НоваяСтрока = ТЗДанные.Добавить();
НоваяСтрока.Идентификатор = Индекс;
НоваяСтрока.Наименование = "Элемент " + Индекс;
НоваяСтрока.Количество = Индекс * 2;
КонецЦикла;
// Помещаем таблицу во временное хранилище с привязкой к форме
// Сохраняем полученный адрес в реквизит формы
АдресХранилищаТЗ = ПоместитьВоВременноеХранилище(ТЗДанные, УникальныйИдентификатор);
КонецПроцедуры
Когда пользователю требуется выполнить действие с данными на сервере, мы считываем и при необходимости модифицируем таблицу:
&НаСервере
Процедура ОбработатьДанныеНаСервере()
// Извлекаем таблицу значений из временного хранилища
ТЗДанные = ПолучитьИзВременногоХранилища(АдресХранилищаТЗ);
Если ТЗДанные <> Неопределено Тогда
// Выполняем требуемые манипуляции на сервере
НайденныеСтроки = ТЗДанные.НайтиСтроки(Новый Структура("Идентификатор", 10));
Для Каждого Стр Из НайденныеСтроки Цикл
Стр.Количество = Стр.Количество + 100;
КонецЦикла;
// Если данные изменились и их нужно сохранить обратно:
// Передаем существующий адрес, чтобы не плодить новые копии в памяти
ПоместитьВоВременноеХранилище(ТЗДанные, АдресХранилищаТЗ);
КонецЕсли;
КонецПроцедуры
Важный нюанс при обновлении данных: обратите внимание, что при повторной записи таблицы во временное хранилище во второй параметр следует передавать ранее сохраненный адрес (АдресХранилищаТЗ), а не УникальныйИдентификатор. Если передавать идентификатор формы повторно, платформа выделит новый адрес и сохранит еще одну копию данных в памяти, что при частых вызовах приведет к утечке памяти сеанса.
Если объем таблицы относительно невелик или данные таблицы непосредственно выводятся в интерфейс пользователю, можно использовать реквизит формы с типом ТаблицаЗначений. В управляемой форме такой объект представляется типом ДанныеФормыКоллекция (или ДанныеФормыДерево).
Рассмотрим, как манипулировать таблицей в контексте формы:
&НаСервере
Процедура ЗаполнитьРеквизитФормыНаСервере()
// Создаем чистую ТаблицуЗначений в серверном контексте
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("Артикул", Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(25)));
ТЗ.Колонки.Добавить("Цена", Новый ОписаниеТипов("Число",,,, Новый КвалификаторыЧисла(15, 2)));
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.Артикул = "ART-001";
НоваяСтрока.Цена = 1500;
// Загружаем данные в реквизит формы "ТаблицаТоваров"
ЗначениеВРеквизитФормы(ТЗ, "ТаблицаТоваров");
КонецПроцедуры
&НаСервере
Процедура ПреобразоватьРеквизитВТаблицуНаСервере()
// Преобразуем реквизит формы обратно в объект ТаблицаЗначений для серверной обработки
ТЗ = РеквизитФормыВЗначение("ТаблицаТоваров");
// Доступны любые серверные методы: Свернуть, Сортировать, НайтиСтроки, ЗагрузитьКолонку и т.д.
ТЗ.Сортировать("Цена Убыв");
// Возвращаем измененные данные обратно в форму
ЗначениеВРеквизитФормы(ТЗ, "ТаблицаТоваров");
КонецПроцедуры
Сравнение подходов:
Если в процессе работы с формой необходимость в таблице значений отпала до закрытия окна (например, пользователь завершил пакетную обработку), рекомендуется явно освободить ресурсы сервера, вызвав удаление из временного хранилища:
&НаСервере
Процедура ОчиститьВременныеДанныеНаСервере()
Если ЗначениеЗаполнено(АдресХранилищаТЗ) Тогда
УдалитьИзВременногоХранилища(АдресХранилищаТЗ);
АдресХранилищаТЗ = "";
КонецЕсли;
КонецПроцедуры
Использование связки метода ПоместитьВоВременноеХранилище с УникальныйИдентификатор формы позволяет полностью изолировать тяжелые структуры данных на стороне сервера, оптимизировать трафик управляемого приложения и предотвратить падения производительности клиентских рабочих станций.