В процессе разработки в среде «1С:Предприятие 8.3» на управляемых формах одной из наиболее частых задач является обмен данными между клиентской частью приложения и сервером. Разработчики регулярно сталкиваются с необходимостью передать коллекцию данных, такую как СписокЗначений, в серверную процедуру для обработки и вернуть её обратно.
В этой статье мы подробно разберем, как устроен этот механизм, какие ограничения накладывает платформа, и изучим все возможные варианты решения этой задачи в зависимости от контекста приложения.
Прежде чем перейти к практическим решениям, проанализируем ситуацию с технической стороны. Главная сложность при передаче данных между клиентом и сервером заключается в процессе сериализации — преобразования объектов 1С в поток данных для отправки по сети и последующего восстановления на другой стороне.
В управляемом приложении тип данных СписокЗначений поддерживает передачу по сети, однако на его содержимое накладываются жесткие ограничения:
СправочникСсылка, ДокументСсылка).СправочникОбъект, таблица значений, карта или неподдерживаемая структура), платформа немедленно заблокирует вызов и сгенерирует ошибку: «Попытка передачи с клиента на сервер мутабельного значения...».СписокЗначений напрямую передавать между клиентом и сервером вообще нельзя. Нам в любом случае придется использовать промежуточные контейнеры данных.Давайте рассмотрим подробные пошаговые варианты решения этой задачи.
Если мы абсолютно уверены, что наш СписокЗначений содержит исключительно простые типы или ссылки, мы можем передать его напрямую в качестве параметра серверной процедуры.
Рассмотрим пример реализации на управляемой форме:
&НаКлиенте
Процедура ОбработатьСписокНаКлиенте()
// Создаем список и заполняем его простыми типами
НовыйСписок = Новый СписокЗначений;
НовыйСписок.Добавить("Товар 1");
НовыйСписок.Добавить("Товар 2");
// Прямая передача на сервер
ОбработатьСписокНаСервере(НовыйСписок);
// Список вернулся измененным
ПоказатьПредупреждение(, "Элементов в списке после сервера: " + НовыйСписок.Количество());
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОбработатьСписокНаСервере(ВходнойСписок)
// Добавляем элемент на стороне сервера
ВходнойСписок.Добавить("Товар 3 на Сервере");
КонецПроцедуры
Важное примечание: Этот способ работает только в управляемом приложении и только при отсутствии мутабельных значений. Если в вашей конфигурации используется обычное приложение, этот код вызовет ошибку выполнения.
Если на сервере нам нужны только сами значения из списка (без учета их представлений, пометок или картинок), лучшим решением по производительности и простоте кода будет выгрузка значений в обычный Массив. Массив — это легкий, универсальный тип данных, который прекрасно сериализуется и передается в любых режимах работы платформы.
Для этого в 1С предусмотрен оптимизированный встроенный метод ВыгрузитьЗначения(). Разберем по шагам, как это реализовать:
&НаКлиенте
Процедура ВыполнитьПередачуЧерезМассив()
// Наш исходный список на клиенте
СписокТоваров = Новый СписокЗначений;
СписокТоваров.Добавить(ПредопределенноеЗначение("Справочник.Номенклатура.ПустаяСсылка"));
// Преобразуем список в массив одной быстрой операцией
МассивЗначений = СписокТоваров.ВыгрузитьЗначения();
// Передаем массив на сервер и получаем результат обратно
НовыйМассив = ОбработатьМассивНаСервере(МассивЗначений);
// Загружаем результат обратно в наш список
СписокТоваров.Очистить();
СписокТоваров.ЗагрузитьЗначения(НовыйМассив);
КонецПроцедуры
&НаСервереБезКонтекста
Функция ОбработатьМассивНаСервере(ВходнойМассив)
// Производим любые манипуляции с массивом на сервере
Результат = Новый Массив;
Для Каждого Элемент Из ВходнойМассив Цикл
// Какая-то логика обработки...
Результат.Добавить(Элемент);
КонецЦикла;
Возврат Результат;
КонецФункции
Этот способ является наиболее рекомендуемым, когда требуется передать только данные, поскольку он минимизирует сетевой трафик, не передавая лишнюю служебную информацию (картинки, пометки и т.д.).
Бывают ситуации, когда на сервере или при возврате на клиент нам критически важно сохранить пользовательские представления элементов, их пометки (флажки) или иные свойства. Для этого используется массив структур. Структура позволяет упаковать все свойства каждого элемента списка и безопасно доставить их на сервер.
Выясним, как выглядит программная реализация этого подхода:
&НаКлиенте
Процедура ПередатьСписокСвойствами()
// Исходный список с пометками и представлениями
СписокСотрудников = Новый СписокЗначений;
СписокСотрудников.Добавить(СсылкаСотрудник1, "Иванов Иван", Истина);
СписокСотрудников.Добавить(СсылкаСотрудник2, "Петров Петр", Ложь);
// Упаковываем список в массив структур
МассивСтруктур = Новый Массив;
Для Каждого ЭлементСписка Из СписокСотрудников Цикл
ДанныеЭлемента = Новый Структура;
ДанныеЭлемента.Вставить("Значение", ЭлементСписка.Значение);
ДанныеЭлемента.Вставить("Представление", ЭлементСписка.Представление);
ДанныеЭлемента.Вставить("Пометка", ЭлементСписка.Пометка);
МассивСтруктур.Добавить(ДанныеЭлемента);
КонецЦикла;
// Отправляем на сервер
РезультатСервера = ОбработатьСтруктурыНаСервере(МассивСтруктур);
// Восстанавливаем список на клиенте
СписокСотрудников.Очистить();
Для Каждого Стр Из РезультатСервера Цикл
СписокСотрудников.Добавить(Стр.Значение, Стр.Представление, Стр.Пометка);
КонецЦикла;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ОбработатьСтруктурыНаСервере(МассивСтруктур)
// На сервере мы можем воссоздать СписокЗначений, если он нужен для серверных методов
СерверныйСписок = Новый СписокЗначений;
Для Каждого Стр Из МассивСтруктур Цикл
СерверныйСписок.Добавить(Стр.Значение, Стр.Представление, Стр.Пометка);
КонецЦикла;
// Выполняем серверные действия...
// Например, отметим всех сотрудников
Для Каждого Элемент Из СерверныйСписок Цикл
Элемент.Пометка = Истина;
КонецЦикла;
// Упаковываем обратно для отправки на клиент
ВыходнойМассив = Новый Массив;
Для Каждого Элемент Из СерверныйСписок Цикл
Данные = Новый Структура("Значение, Представление, Пометка", Элемент.Значение, Элемент.Представление, Элемент.Пометка);
ВыходнойМассив.Добавить(Данные);
КонецЦикла;
Возврат ВыходнойМассив;
КонецФункции
Если мы работаем в управляемой форме и нам нужно обрабатывать список значений в серверных процедурах этой же формы, нам вообще не обязательно передавать его в качестве параметра! Платформа «1С:Предприятие 8.3» умеет делать это автоматически.
Для этого выполним следующие действия:
СписокНаФорме) и зададим тип данных СписокЗначений.Теперь мы можем использовать контекстный вызов сервера. Платформа самостоятельно выполнит сериализацию реквизита при переходе через границу клиента и сервера:
&НаКлиенте
Процедура КомандаОбработатьКонтекстно(Команда)
// Заполняем реквизит формы напрямую на клиенте
СписокНаФорме.Очистить();
СписокНаФорме.Добавить("Клиентское значение");
// Вызываем контекстный серверный метод
ОбработатьДанныеФормыНаСервере();
// Здесь данные реквизита "СписокНаФорме" уже обновлены сервером
КонецПроцедуры
&НаСервере
Процедура ОбработатьДанныеФормыНаСервере()
// Мы имеем прямой доступ к реквизиту формы на сервере
СписокНаФорме.Добавить("Серверное значение");
КонецПроцедуры
Важно помнить о производительности: Контекстный серверный вызов (директива &НаСервере) передает на сервер состояние всей формы полностью. Если форма содержит тяжелые таблицы или много реквизитов, частые контекстные вызовы могут замедлить работу интерфейса. Используйте этот метод разумно.
Представим ситуацию, когда список содержит десятки тысяч элементов (например, перечень уникальных идентификаторов или кодов маркировки). Передача такого объема данных через параметры методов или контекст формы приведет к существенному зависанию интерфейса, так как сериализация огромных массивов нагружает процессор и сеть.
В таких сценариях оптимальным решением является использование Временного хранилища. Данные помещаются в оперативную память сервера один раз, а между клиентом и сервером передается лишь короткая текстовая строка — адрес во временном хранилище.
Посмотрим на пример реализации данного алгоритма:
&НаКлиенте
Процедура ПередатьБольшойСписок()
// Предположим, у нас есть огромный список значений
БольшойСписок = ПолучитьОгромныйСписокДанных();
// Помещаем его во временное хранилище напрямую с клиента
// УникальныйИдентификатор — это стандартный идентификатор текущей формы
АдресХранилища = ПоместитьВоВременноеХранилище(БольшойСписок, УникальныйИдентификатор);
// Передаем на сервер только адрес (строку)
ОбработатьТяжелыеДанныеНаСервере(АдресХранилища);
// Получаем измененный список обратно из хранилища
ИзмененныйСписок = ПолучитьИзВременногоХранилища(АдресХранилища);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОбработатьТяжелыеДанныеНаСервере(АдресХранилища)
// Извлекаем наш список на сервере по адресу
СписокНаСервере = ПолучитьИзВременногоХранилища(АдресХранилища);
// Выполняем тяжелую серверную обработку данных
СписокНаСервере.Добавить("Дополнительный элемент безопасности");
// Записываем результат обратно в то же временное хранилище
ПоместитьВоВременноеХранилище(СписокНаСервере, АдресХранилища);
КонецПроцедуры
Этот способ позволяет избежать избыточного копирования тяжелых структур данных в оперативной памяти и существенно повышает скорость отклика интерфейса программы.
Подводя итог нашему анализу, структурируем полученные знания:
Массив (Вариант 2).ВыгрузитьЗначения() (Вариант 2).Массив структур (Вариант 3).Временное хранилище (Вариант 5) для оптимизации трафика и быстродействия системы.