Как правильно передать Таблицу Значений с клиента на сервер в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на платформе 1С:Предприятие 8.3 с использованием управляемых форм мы часто сталкиваемся с задачей передачи данных между клиентской частью (где работает пользователь) и серверной частью (где выполняется бизнес-логика и работа с базой данных). Одной из самых распространенных ошибок, возникающих у разработчиков, является попытка передать объект ТаблицаЗначений напрямую с клиента на сервер.

В этой статье мы подробно разберем, почему возникает ошибка передачи мутабельного значения, проанализируем архитектурные ограничения платформы и рассмотрим надежные способы решения этой задачи, основанные на обсуждениях профессионального сообщества и документации.

Постановка проблемы: Ошибка передачи мутабельного значения

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


&НаКлиенте
Процедура ВыполнитьОбработку()
    ТЗ = Новый ТаблицаЗначений; 
    // ... заполнение ТЗ ...
    ОбработатьНаСервере(ТЗ); // Здесь возникнет ошибка!
КонецПроцедуры

&НаСервере
Процедура ОбработатьНаСервере(ТЗ)
    // Логика обработки
КонецПроцедуры

В момент вызова серверной процедуры платформа выдаст ошибку. Суть проблемы заключается в том, что объект ТаблицаЗначений в 1С существует только на сервере и в толстом клиенте. В тонком клиенте (управляемом приложении) такого типа данных полноценно не существует, и, что более важно, этот объект является мутабельным (изменяемым), поэтому платформа не может гарантировать его корректную XDTO-сериализацию для передачи через веб-сервер или кластер серверов.

Разберем детально два основных способа решения этой проблемы, которые мы выделили в ходе анализа обсуждения.

Способ 1: Использование Временного хранилища (Рекомендуемый метод)

Этот метод является наиболее универсальным и производительным, особенно если таблица содержит большое количество строк. Механизм ВременноеХранилище позволяет поместить данные в специальную область памяти на сервере (или временно отправить их с клиента на сервер), получить уникальный адрес, и затем по этому адресу получить данные в другом месте.

Давайте рассмотрим этот процесс пошагово.

Шаг 1. Подготовка данных на клиенте

Так как на тонком клиенте объект ТаблицаЗначений недоступен, мы не можем создать его напрямую. Однако, если у вас данные уже находятся в реквизите формы (например, ДанныеФормыКоллекция), их можно передать. Если же мы собираем данные программно, то на клиенте мы обычно оперируем массивами структур. Но чтобы воспользоваться мощью временного хранилища для передачи именно таблицы, нам часто приходится сначала сформировать данные на сервере (в контексте неконтекстного вызова) или передавать через хранилище структуру.

Однако, рассмотрим сценарий, когда мы хотим передать сложную структуру данных. Мы помещаем данные во временное хранилище. Обратите внимание: метод ПоместитьВоВременноеХранилище доступен и на клиенте, и на сервере.

Посмотрим на пример кода, где мы передаем данные через адрес:


&НаКлиенте
Процедура ЗапуститьОбработку()
    
    // Допустим, у нас есть массив структур с данными на клиенте
    МассивДанных = Новый Массив;
    СтрокаДанных = Новый Структура("Товар, Количество", "Товар1", 10);
    МассивДанных.Добавить(СтрокаДанных);
    
    // 1. Помещаем данные во временное хранилище
    // УникальныйИдентификатор формы передаем, чтобы данные жили, пока жива форма
    АдресХранилища = ПоместитьВоВременноеХранилище(МассивДанных, УникальныйИдентификатор);
    
    // 2. Вызываем серверную процедуру, передавая ТОЛЬКО адрес (строку)
    ОбработатьДанныеНаСервере(АдресХранилища);
    
КонецПроцедуры

Шаг 2. Получение данных на сервере

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


&НаСервере
Процедура ОбработатьДанныеНаСервере(АдресВХ)
    
    // 1. Получаем данные из хранилища
    ПолученныеДанные = ПолучитьИзВременногоХранилища(АдресВХ);
    
    // Проверяем, что данные есть
    Если ПолученныеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // 2. Если мы передавали массив структур, можно сконвертировать его в ТЗ
    // (функцию преобразования массива в ТЗ часто приходится писать вручную или использовать БСП)
    НашаТЗ = ПреобразоватьМассивВТаблицу(ПолученныеДанные);
    
    // Теперь у нас есть полноценная ТаблицаЗначений
    // Можно, например, загрузить её во временную таблицу запроса
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ * ПОМЕСТИТЬ ВТ_Данные ИЗ &ТЗ КАК ТЗ";
    Запрос.УстановитьПараметр("ТЗ", НашаТЗ);
    // ... выполнение запроса ...
    
КонецПроцедуры

Если же задача состоит в том, чтобы передать именно ТаблицаЗначений, которая была сформирована в другом серверном вызове и вернулась на клиент "неявно" (что невозможно), то обычно цепочка выглядит так: Сервер (создал ТЗ) -> Поместил в Хранилище -> Вернул Адрес на Клиент -> Клиент передал Адрес в другую процедуру Сервера.

Способ 2: Использование Массива Структур (Сериализация)

Если объем данных невелик (например, пользователь выбрал несколько строк в динамическом списке или заполнил небольшую табличную часть), использование временного хранилища может показаться избыточным. В этом случае мы можем использовать типы данных, которые легко сериализуются в XDTO и "проходят" между клиентом и сервером. Самый распространенный вариант — Массив, содержащий объекты типа Структура.

Проанализируем этот подход. Структура в 1С является аналогом строки таблицы, где ключ — это имя колонки, а значение — содержимое ячейки.

Реализация на клиенте:


&НаКлиенте
Процедура ОтправитьДанные()
    
    МассивДляПередачи = Новый Массив;
    
    // Формируем структуры (строки будущей таблицы)
    Стр1 = Новый Структура("Номенклатура, Цена", ПредопределенноеЗначение("Справочник.Номенклатура.ПустаяСсылка"), 100);
    МассивДляПередачи.Добавить(Стр1);
    
    Стр2 = Новый Структура("Номенклатура, Цена", ПредопределенноеЗначение("Справочник.Номенклатура.ПустаяСсылка"), 200);
    МассивДляПередачи.Добавить(Стр2);
    
    // Передаем массив напрямую
    ПринятьДанныеНаСервере(МассивДляПередачи);
    
КонецПроцедуры

Реализация на сервере:

На сервере мы принимаем массив. Если нам нужна именно ТаблицаЗначений (например, для использования метода ЗагрузитьКолонку или для запросов), нам придется сконвертировать массив обратно в таблицу.


&НаСервере
Процедура ПринятьДанныеНаСервере(МассивСтруктур)
    
    // Создаем таблицу значений
    ТЗ = Новый ТаблицаЗначений;
    // Нам нужно знать структуру колонок. 
    // Вариант А: Знаем заранее
    ТЗ.Колонки.Добавить("Номенклатура");
    ТЗ.Колонки.Добавить("Цена");
    
    // Вариант Б: Определяем по первой строке массива (если он не пуст)
    // Это более универсальный метод
    Если МассивСтруктур.Количество() > 0 Тогда
        Для Каждого КлючИЗначение Из МассивСтруктур[0] Цикл
             ТЗ.Колонки.Добавить(КлючИЗначение.Ключ);
        КонецЦикла;
    КонецЕсли;
    
    // Заполняем данными
    Для Каждого СтрСтруктура Из МассивСтруктур Цикл
        НоваяСтрока = ТЗ.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрСтруктура);
    КонецЦикла;
    
    // ТЗ готова к работе
КонецПроцедуры

Этот метод прост и нагляден, но стоит помнить о производительности. Если вы попытаетесь передать массив из 10 000 структур, это вызовет существенный трафик и нагрузку на сериализацию. В таких случаях всегда выбирайте Способ 1 (Временное хранилище).

Особенности работы с Реквизитами Формы

Часто возникает ситуация, когда "таблица" уже есть на форме — это реквизит типа ТаблицаЗначений (в редакторе форм), который на самом деле является типом ДанныеФормыКоллекция на клиенте. Важно понимать, как работать с ним.

Если вы передаете ДанныеФормыКоллекция в серверную процедуру без контекста (&НаСервереБезКонтекста), платформа не сможет принять этот тип данных напрямую, так как он привязан к управляемой форме. В этом случае его тоже нужно либо преобразовать в массив структур, либо использовать контекстный вызов.

При использовании вызова &НаСервере (с контекстом формы) передавать таблицу параметром вообще не обязательно — она и так видна на сервере как реквизит формы (ЭтаФорма.МояТаблица или Объект.МояТаблица). В этом случае платформа автоматически преобразует ДанныеФормыКоллекция в ДанныеФормыСтруктураСКоллекцией или аналогичные серверные обертки, с которыми можно работать как с коллекцией. Чтобы превратить реквизит формы в настоящую ТаблицаЗначений на сервере, используем метод:


&НаСервере
Процедура РаботаСКонтекстом()
    // Преобразование данных формы в прикладной объект (ТЗ)
    ТЗ = РеквизитФормыВЗначение("МояТаблицаНаФорме");
    
    // ... работаем с ТЗ ...
    
    // Если нужно вернуть изменения обратно на форму
    ЗначениеВРеквизитФормы(ТЗ, "МояТаблицаНаФорме");
КонецПроцедуры

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

Давайте подведем итог. При необходимости передать табличные данные с клиента на сервер, мы рекомендуем придерживаться следующего алгоритма выбора решения:

  1. Если данные уже являются реквизитом формы и вы используете контекстный вызов (&НаСервере) — используйте методы РеквизитФормыВЗначение.
  2. Если данных много (сотни и тысячи строк) или вы используете вызов без контекста — используйте Временное хранилище. Это стандарт де-факто для высокопроизводительных систем.
  3. Если данных мало и они формируются динамически на клиенте — используйте Массив Структур. Это простой и понятный способ, не требующий управления адресами хранилища.

Избегайте попыток "обмануть" платформу и передать мутабельные объекты напрямую. Понимание механизмов клиент-серверного взаимодействия в 1С 8.3 — ключ к созданию стабильных и быстрых приложений.

← На главную