Разработка клиент-серверных приложений в платформе 1С:Предприятие 8 требует четкого понимания границ исполнения кода. Одна из самых частых задач, с которой сталкиваются программисты — это необходимость передать данные, заполненные пользователем в интерфейсе, на сервер для последующей записи в базу данных или сложной обработки. Табличная часть документа или справочника является классическим примером таких данных, которые формируются на клиенте, но должны быть сохранены в информационной базе.

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

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

Архитектура клиент-серверного взаимодействия в 1С

Платформа 1С:Предприятие построена по клиент-серверной архитектуре, где код может выполняться либо на стороне тонкого клиента, либо на стороне сервера приложений. Граница между этими мирами проходит строго по директивам компиляции &НаКлиенте и &НаСервере. Передача данных через эту границу — это не просто копирование переменных в памяти, а процесс сериализации (преобразования в поток байтов) и десериализации. Именно поэтому передача сложных объектов, таких как табличная часть, требует особого внимания.

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

⚠️ Внимание: Передача больших табличных частей (сотни и тысячи строк) в параметрах серверного вызова может существенно увеличить сетевой трафик и время отклика интерфейса. Старайтесь минимизировать объем передаваемых данных или используйте пакетную обработку.

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

📊 Какой метод передачи данных вы используете чаще всего?
Прямая передача объекта
Передача через Параметры
Использование Глобальных переменных
Запись в временное хранилище

Передача через параметры серверных процедур

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

При вызове серверного метода из клиентского события, например, из кнопки формы, вы можете передать сам объект документа целиком. В этом случае табличные части будут переданы автоматически как вложенные свойства. Однако, если вам нужно передать только данные таблицы без лишнего «веса» всего документа, лучше использовать промежуточные структуры. Вы можете выгрузить табличную часть в массив структур или таблицу значений и передать именно этот массив.

Рассмотрим пример кода, где мы передаем данные для проверки. На клиенте мы формируем структуру, содержащую необходимую информацию, и вызываем серверный метод:

&НаКлиенте

Процедура КнопкаПроверитьДанные(Команда)

ПараметрыПроверки = Новый Структура;

ПараметрыПроверки.Вставить("ТаблицаТоваров", Объект.Товары);

ПараметрыПроверки.Вставить("ДатаДокумента", Объект.Дата);

// Вызов серверной процедуры с передачей структуры

ПроверитьКорректностьНаСервере(ПараметрыПроверки);

КонецПроцедуры

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

💡

При передаче больших объемов данных старайтесь не включать в структуру лишние реквизиты. Передавайте только те поля табличной части, которые действительно необходимы для серверной логики. Это ускорит сериализацию.

Работа с объектами метаданных и ссылками

Особенность платформы заключается в различии между объектом в памяти и ссылкой на объект в базе данных. Когда вы работаете с формой нового документа, объект существует только в оперативной памяти клиента. При передаче такого объекта на сервер, платформа создает его серверную копию. Любые изменения, внесенные сервером в свойства этого объекта (например, в табличную часть), останутся только в серверной копии, если не вернуть объект обратно.

Если же документ уже записан в базу, и вы передаете его ссылку (ДокументСсылка), ситуация меняется. Серверная процедура может получить объект по ссылке с помощью метода ПолучитьОбъект(). В этом случае все изменения, внесенные в табличные части на сервере, будут применены к реальному объекту в базе данных после вызова метода Записать(). Это фундаментальное различие часто становится источником ошибок у начинающих разработчиков.

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

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

⚠️ Внимание: Если вы передаете объект нового документа на сервер, изменяете его табличную часть и не возвращаете объект клиенту, все изменения потеряются при завершении серверного вызова. Клиентский объект останется в исходном состоянии.

Использование временного хранилища для больших данных

Когда объем данных в табличной части становится критически большим, передача их через параметры вызова становится неэффективной. Сериализация и передача мегабайтов данных по сети могут «подвесить» интерфейс пользователя. В таких случаях архитектурно правильным решением является использование Временного Хранилища (Temporary Storage). Этот механизм позволяет сохранить данные на сервере и передать клиенту лишь легкий идентификатор (уникальный ключ).

Схема работы выглядит следующим образом: клиент помещает тяжелую табличную часть или массив данных во временное хранилище с помощью метода ПоместитьВоВременноеХранилище. Метод возвращает уникальный идентификатор типа УникальныйИдентификатор. Этот идентификатор, занимающий всего 16 байт, передается на сервер в качестве параметра. Серверная процедура извлекает данные по этому ключу с помощью ПолучитьИзВременногоХранилища.

&НаКлиенте

Процедура ОбработкаБольшихДанных(Команда)

// Получаем данные из формы

ДанныеДляОбработки = Объект.Товары.Выгрузить();

// Помещаем во временное хранилище

Идентификатор = ПоместитьВоВременноеХранилище(ДанныеДляОбработки, УникальныйИдентификатор.Новый());

// Передаем только ключ на сервер

ОбработатьНаСервере(Идентификатор);

КонецПроцедуры

&НаСервере

Процедура ОбработатьНаСервере(ИдентификаторХранилища)

// Извлекаем данные на сервере

Данные = ПолучитьИзВременногоХранилища(ИдентификаторХранилища);

// Выполняем тяжелые вычисления...

КонецПроцедуры

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

☑️ Алгоритм работы с большими данными

Выполнено: 0 / 5

Глобальные переменные и контекст сеанса

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

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

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

Почему глобальные переменные не работают «из коробки»?

Глобальные переменные на клиенте хранятся в памяти процесса клиента (thin client), а на сервере — в памяти процесса сервера 1С (rphost). Между ними нет автоматической синхронизации. Чтобы данные попали на сервер, их нужно явно передать через параметры вызова или сохранить в хранилище, доступное из серверного контекста.

Обработка ошибок и валидация данных

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

При возникновении ошибок валидации на сервере необходимо корректно сообщить об этом пользователю. Просто вызвать Сообщить() из серверного кода нельзя, так как этот метод работает только в клиентском контексте. Правильный подход — перехватить ошибку или сформировать текст сообщения на сервере, вернуть его клиенту в виде результата функции или выбросить исключение, которое будет обработано в блоке Попытка...Исключение на клиенте.

Пример обработки ситуации, когда в табличной части обнаружен товар с недопустимой ценой:

&НаСервере

Функция ПроверитьЦены(ТаблицаТоваров)

Для Каждого СтрокаТовара Из ТаблицаТоваров Цикл

Если СтрокаТовара.Цена < 0 Тогда

// Формируем понятное сообщение об ошибке

Возврат "Обнаружена отрицательная цена для товара: " + СтрокаТовара.Номенклатура;

КонецЕсли;

КонецЦикла;

Возврат ""; // Ошибок нет

КонецФункции

⚠️ Внимание: Никогда не доверяйте данным, пришедшим с клиента. Даже если в форме стоят проверки, опытный пользователь или сбой в работе ПО могут отправить некорректные данные. Всегда дублируйте критические проверки на сервере перед записью в базу.

💡

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

Оптимизация производительности при записи

После того как табличная часть успешно передана на сервер и прошла валидацию, следующим этапом является ее запись. Если вы работаете с объектом документа, избегайте построчной записи изменений, если это возможно. Платформа оптимизирована для работы с объектом целиком. Модификация табличной части в цикле с промежуточными записями — это грубая ошибка, ведущая к деградации производительности.

Используйте методы оптимизированного доступа к табличным частям. Например, если вам нужно добавить множество строк, сформированных на основе каких-то расчетов, создайте новую таблицу значений на сервере, заполните ее и затем одним вызовом ЗагрузитьКолонки или присваиванием перенесите данные в табличную часть объекта. Это минимизирует количество обращений к механизму регистрации изменений.

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

Как ускорить загрузку тысяч строк?

Вместо цикла "Для каждого" и добавления строк по одной, используйте метод ТабличнаяЧасть.ЗагрузитьКолонки(). Подготовьте массивы данных для каждой колонки отдельно и загрузите их одним вызовом. Это может ускорить процесс в 10-20 раз по сравнению с построчным добавлением.

Можно ли передать табличную часть, если документ еще не записан?

Да, можно. В этом случае на сервер передается объект типа ДокументОбъект в состоянии «новый». Сервер может манипулировать его табличными частями, добавлять и удалять строки. Однако, чтобы изменения сохранились, объект должен быть возвращен из серверной процедуры клиенту (если вызов не в рамках одной транзакции с последующей записью) или записан непосредственно на сервере с последующей выгрузкой актуальных данных на форму.

В чем разница между передачей ТаблицыЗначений и массива Структур?

С точки зрения передачи параметров разницы практически нет, оба типа сериализуются эффективно. Однако ТаблицаЗначений обладает более строгой типизацией колонок и встроенными методами сортировки, поиска и фильтрации, что делает её более удобной для обработки на сервере. Массив структур более гибок по структуре, но требует больше кода для однотипных операций над строками.

Что делать, если при передаче возникает ошибка «Сериализация не поддерживается»?

Эта ошибка возникает, если вы пытаетесь передать объект, который не предназначен для передачи через границу клиент-сервер. Например, некоторые объекты метаданных, объекты COM или специфические клиентские формы не могут быть сериализованы. Решение — выгрузить нужные данные из такого объекта в примитивные типы (Структура, ТаблицаЗначений, Массив) и передавать уже их.

Как передать табличную часть из формы списка на сервер?

В форме списка нет объекта документа в явном виде. Вам нужно сначала получить выделенные строки, затем для каждой строки получить ссылку на документ, вызвать ПолучитьОбъект() (желательно пакетно, если версия платформы позволяет, или в цикле с оптимизацией), извлечь табличные части и передать их на сервер, например, собрав в общую таблицу значений для пакетной обработки.