В процессе оптимизации клиент-серверного взаимодействия в управляемом приложении 1С разработчики часто стремятся перевести серверные вызовы в директиву &НаСервереБезКонтекста. При этом возникает типичная задача: как передать таблицу с клиента на сервер, выполнить над ней сложные манипуляции (например, через объект ТаблицаЗначений) и вернуть обновленные строки обратно в реквизит формы.
Проанализируем предложенный автором темы подход, выясним, почему в исходном коде присутствуют избыточные конструкции, и разберем наиболее эффективные и правильные способы решения этой задачи.
Рассмотрим код, предложенный в вопросе:
&НаКлиенте
Процедура Пр1(Команда)
КопироватьДанныеФормы(Пр2(Реквизит1), Реквизит1);
КонецПроцедуры
&НаСервереБезКонтекста
Функция Пр2(Знач Реквизит1)
ТабЗнч = ДанныеФормыВЗначение(Реквизит1, Тип("ТаблицаЗначений"));
// Выполняются действия над таблицей значений
ЗначениеВДанныеФормы(ТабЗнч, Реквизит1);
Возврат Реквизит1;
КонецФункции
Разберем детально, что здесь происходит и какие ошибки или неоптимальности допущены:
ДанныеФормыВЗначение() и ЗначениеВДанныеФормы() являются глобальными процедурами/функциями платформы 1С. В отличие от РеквизитФормыВЗначение() и ЗначениеВРеквизитФормы(), они не требуют контекста формы и штатно выполняются во внеконтекстных вызовах и общих модулях. Поэтому вызов этих методов в процедуре &НаСервереБезКонтекста абсолютно корректен.Знач): Ключевое слово Знач заставляет платформу создать изолированную копию коллекции. Из-за этого изменения внутри функции не передаются обратно автоматически, и разработчику приходится возвращать значение функцией и вручную вызывать КопироватьДанныеФормы() на клиенте.&НаСервереБезКонтекста с передачей коллекции ДанныеФормыКоллекция вся таблица целиком сериализуется в XML/JSON и отправляется на сервер, а затем целиком возвращается обратно. Если таблица содержит много строк, такой вызов может оказаться значительно «тяжелее» и медленнее обычного контекстного вызова.Если мы хотим оставить внеконтекстный вызов, но избавиться от лишнего дублирования и ручного копирования данных на клиенте, уберем ключевое слово Знач и заменим функцию на процедуру. В этом случае платформа автоматически организует двустороннюю передачу данных:
&НаКлиенте
Процедура ОбработатьТаблицу(Команда)
ОбработатьТаблицуНаСервереБезКонтекста(Реквизит1);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ОбработатьТаблицуНаСервереБезКонтекста(Реквизит1)
// Преобразуем ДанныеФормыКоллекция в ТаблицуЗначений
ТабЗнч = ДанныеФормыВЗначение(Реквизит1, Тип("ТаблицаЗначений"));
// Выполняем действия над ТаблицейЗначений (сортировка, расчеты, итоги)
// ...
// Загружаем обновленные данные обратно в параметр
ЗначениеВДанныеФормы(ТабЗнч, Реквизит1);
КонецПроцедуры
Посмотрим, почему этот вариант работает чище: платформа передает коллекцию на сервер, метод ЗначениеВДанныеФормы() перезаполняет переданную коллекцию Реквизит1, и после завершения вызова изменения автоматически отображаются в реквизите формы на клиенте без дополнительных вызовов КопироватьДанныеФормы().
Как справедливо отмечено в обсуждении темы, не следует доводить стремление к &НаСервереБезКонтекста до фанатизма. При стандартном контекстном вызове &НаСервере платформа использует оптимизированный механизм передачи дельты (только измененных данных формы), что часто работает быстрее, чем полная сериализация большой коллекции во внеконтекстный метод.
&НаКлиенте
Процедура ОбработатьТаблицу(Команда)
ОбработатьТаблицуНаСервере();
КонецПроцедуры
&НаСервере
Процедура ОбработатьТаблицуНаСервере()
ТабЗнч = РеквизитФормыВЗначение("Реквизит1");
// Обработка данных таблицы значений
// ...
ЗначениеВРеквизитФормы(ТабЗнч, "Реквизит1");
КонецПроцедуры
Данный подход является стандартным и наиболее читаемым при разработке управляемых форм, если не требуется изолировать логику в повторно используемый общий модуль.
Если таблица содержит сотни или тысячи строк, передавать всю коллекцию между клиентом и сервером крайне неэффективно. В таких сценариях рекомендуются следующие архитектурные подходы:
ТаблицаЗначений во временное хранилище с помощью метода ПоместитьВоВременноеХранилище(), передав на клиент только строковый адрес, а отображение организуем порционно или по мере необходимости.