В процессе разработки на платформе 1С:Предприятие 8.3, особенно при работе с управляемыми формами, мы часто сталкиваемся с необходимостью обмена данными между серверным и клиентским контекстами. Представьте ситуацию: вы выполнили сложный запрос на сервере, получили ценную выборку данных (объект Выборка), и теперь вам нужно отобразить эти данные пользователю на форме или использовать их для заполнения реквизитов формы. Однако, объект Выборка является серверным и не может быть напрямую передан на клиент.
Давайте разберемся, как мы можем эффективно и правильно решить эту задачу, передавая результаты запросов с сервера на клиент, сохраняя при этом производительность и избегая типичных ошибок.
Прежде чем погружаться в конкретные методы, давайте еще раз вспомним, как устроено клиент-серверное взаимодействие в 1С:Предприятие 8.3. В управляемых формах существует четкое разделение обязанностей:
Хотя объекты формы, регистров сведений и документов физически существуют на сервере, их визуальное представление и возможность взаимодействия с ними реализуются на клиенте. Это означает, что для отображения данных из регистра сведений или для заполнения полей документа через пользовательский интерфейс, данные должны пройти через границу клиент-сервер. Дополнительно изучить принципы разделения логики поможет материал об управлении местом выполнения кода в управляемых формах.
Когда мы получаем Выборку запроса на сервере, она находится в серверном контексте. Если нам нужно отобразить данные из этой выборки на форме (например, в табличной части, в полях ввода) или использовать их для дальнейшей обработки на клиенте, мы обязательно должны перевести их в формат, который платформа может сериализовать и передать через сеть.
Существует несколько проверенных и эффективных способов передачи данных с сервера на клиент. Мы рассмотрим наиболее распространенные из них, начиная с наиболее часто используемых.
Это основной и наиболее рекомендуемый подход для передачи произвольных данных. Платформа 1С позволяет сериализовать и передавать между клиентом и сервером определенные типы коллекций, которые могут хранить данные из вашей Выборки.
Массив структур – это универсальный и гибкий способ передачи данных. Мы можем преобразовать каждую строку вашей Выборки в объект Структура, где ключами будут имена полей выборки, а значениями – соответствующие данные. Затем эти структуры собираются в Массив. Этот метод особенно удобен, когда у вас есть произвольный набор полей и вы хотите сохранить их именованными.
Рассмотрим пример, как преобразовать Выборку в Массив Структур:
// Код выполняется на сервере (например, в функции с аннотацией &НаСервере)
Функция ПолучитьДанныеДляФормыНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РегистрСведений.НазваниеРегистра.Измерение1 КАК Измерение1,
| РегистрСведений.НазваниеРегистра.Ресурс1 КАК Ресурс1
|ИЗ
| РегистрСведений.НазваниеРегистра КАК РегистрСведенийНазваниеРегистра";
Выборка = Запрос.Выполнить().Выбрать();
МассивСтруктур = Новый Массив;
Пока Выборка.Следующий() Цикл
СтруктураДанных = Новый Структура;
СтруктураДанных.Вставить("Измерение1", Выборка.Измерение1);
СтруктураДанных.Вставить("Ресурс1", Выборка.Ресурс1);
// Добавляем другие поля по аналогии
МассивСтруктур.Добавить(СтруктураДанных);
КонецЦикла;
Возврат МассивСтруктур;
КонецФункции
// Код выполняется на клиенте (например, в обработчике команды формы)
Процедура ЗагрузитьДанныеНаКлиенте()
МассивДанных = ПолучитьДанныеДляФормыНаСервере(); // Вызов серверной функции
// Теперь мы можем работать с МассивДанных на клиенте
// Например, вывести в СообщениеПользователю или заполнить табличную часть
Для Каждого СтрокаДанных Из МассивДанных Цикл
Сообщить("Измерение: " + СтрокаДанных.Измерение1 + ", Ресурс: " + СтрокаДанных.Ресурс1);
КонецЦикла;
КонецПроцедуры
Таблица значений – это еще один очень распространенный способ передачи данных, особенно когда нам нужно работать с табличными данными, которые затем будут отображены в табличных частях формы. ТаблицаЗначений может быть напрямую привязана к реквизиту формы с типом ДинамическийСписок или ТаблицаЗначений.
Давайте посмотрим, как можно преобразовать Выборку в ТаблицуЗначений:
// Код выполняется на сервере
Функция ПолучитьТаблицуДанныхНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РегистрСведений.НазваниеРегистра.Измерение1 КАК Измерение1,
| РегистрСведений.НазваниеРегистра.Ресурс1 КАК Ресурс1
|ИЗ
| РегистрСведений.НазваниеРегистра КАК РегистрСведенийНазваниеРегистра";
Выборка = Запрос.Выполнить().Выбрать();
ТаблицаДанных = Новый ТаблицаЗначений;
// Определяем колонки ТаблицыЗначений
ТаблицаДанных.Колонки.Добавить("Измерение1");
ТаблицаДанных.Колонки.Добавить("Ресурс1");
// Добавляем другие колонки по аналогии
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТаблицаДанных.Добавить();
НоваяСтрока.Измерение1 = Выборка.Измерение1;
НоваяСтрока.Ресурс1 = Выборка.Ресурс1;
// Заполняем другие поля по аналогии
КонецЦикла;
Возврат ТаблицаДанных;
КонецФункции
// Код выполняется на клиенте
Процедура ЗагрузитьДанныеВТаблицуФормы()
ТаблицаДанныхССервера = ПолучитьТаблицуДанныхНаСервере(); // Вызов серверной функции
// Предположим, у нас есть реквизит формы с именем "МояТаблица" типа ТаблицаЗначений
МояТаблица.Загрузить(ТаблицаДанныхССервера);
// Или если это динамический список, можем обновить его
КонецПроцедуры
Обратите внимание, что ТаблицаЗначений более эффективно подходит для прямого отображения в табличных частях формы, так как ее структура (колонки) соответствует структуре табличной части.
СписокЗначений и Соответствие также являются сериализуемыми коллекциями, но они подходят для более специфичных задач. СписокЗначений удобен для передачи набора уникальных значений (например, списка идентификаторов для фильтрации), а Соответствие – для передачи данных в формате "ключ-значение", когда ключи имеют простой тип (строка, число).
Пример использования СписокЗначений:
// На сервере
Функция ПолучитьСписокИменНоменклатурыНаСервере()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура.Наименование ИЗ Справочник.Номенклатура КАК Номенклатура";
Выборка = Запрос.Выполнить().Выбрать();
СписокИмен = Новый СписокЗначений;
Пока Выборка.Следующий() Цикл
СписокИмен.Добавить(Выборка.Наименование);
КонецЦикла;
Возврат СписокИмен;
КонецФункции
// На клиенте
Процедура ЗаполнитьПолеСпискомИмен()
МойСписокИмен = ПолучитьСписокИменНоменклатурыНаСервере();
// Далее можно использовать МойСписокИмен, например, для заполнения поля ввода типа "Список выбора"
Сообщить("Первое имя в списке: " + МойСписокИмен.Получить(0).Значение);
КонецПроцедуры
Пример использования Соответствие:
// На сервере
Функция ПолучитьСоответствиеКодовИНаименованийНаСервере()
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Номенклатура.Код, Номенклатура.Наименование ИЗ Справочник.Номенклатура КАК Номенклатура";
Выборка = Запрос.Выполнить().Выбрать();
СоответствиеДанных = Новый Соответствие;
Пока Выборка.Следующий() Цикл
СоответствиеДанных.Вставить(Выборка.Код, Выборка.Наименование);
КонецЦикла;
Возврат СоответствиеДанных;
КонецФункции
// На клиенте
Процедура ИспользоватьСоответствие()
МоеСоответствие = ПолучитьСоответствиеКодовИНаименованийНаСервере();
Сообщить("Наименование для кода '00001': " + МоеСоответствие["00001"]);
КонецПроцедуры
Платформа 1С поддерживает нативную сериализацию прикладных типов в форматы JSON (JavaScript Object Notation) и XML (Extensible Markup Language). Этот подход может быть чрезвычайно полезен, когда мы хотим передать сложные структуры данных, или когда нам нужна стандартизированная передача, в том числе для взаимодействия с внешними системами. JSON обычно более компактен и быстрее в сериализации/десериализации по сравнению с XML.
Передача данных через JSON:
// Код выполняется на сервере
Функция ПолучитьДанныеВФорматеJSONНаСервере()
// Предположим, у нас уже есть МассивСтруктур или ТаблицаЗначений
// Мы можем преобразовать его в JSON-строку
МассивДляJSON = Новый Массив; // Или ТаблицаЗначений
// ... (заполняем МассивДляJSON или ТаблицаЗначений) ...
// Пример заполнения Массива для JSON
Структура1 = Новый Структура("Поле1, Поле2", "Значение1", 123);
Структура2 = Новый Структура("Поле1, Поле2", "Значение3", 456);
МассивДляJSON.Добавить(Структура1);
МассивДляJSON.Добавить(Структура2);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, МассивДляJSON);
СтрокаJSON = ЗаписьJSON.Закрыть();
Возврат СтрокаJSON;
КонецФункции
// Код выполняется на клиенте
Процедура ОбработатьJSONНаКлиенте()
СтрокаJSONССервера = ПолучитьДанныеВФорматеJSONНаСервере(); // Вызов серверной функции
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSONССервера);
// ПрочитатьJSON вернет МассивСтруктур или ТаблицуЗначений, в зависимости от того, что было сериализовано
ДанныеИзJSON = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
// Теперь ДанныеИзJSON - это МассивСтруктур, который можно использовать на клиенте
Для Каждого Элемент Из ДанныеИзJSON Цикл
Сообщить("Поле1: " + Элемент.Поле1 + ", Поле2: " + Элемент.Поле2);
КонецЦикла;
КонецПроцедуры
Помимо JSON, мы также можем использовать XML-сериализацию с помощью объектов ЗаписьXML и ЧтениеXML, если это требуется для совместимости или по другим причинам.
Способ вызова серверной процедуры или функции также влияет на передачу данных. Мы используем аннотации, чтобы указать, в каком контексте будет выполняться код.
При использовании аннотации &НаСервере (или ее аналога без явного указания, если функция/процедура формы) на сервер передается весь контекст формы, включая все ее реквизиты и элементы. Это удобно, если на сервере нам нужно работать с данными всей формы (например, пересчитать табличную часть, изменить несколько реквизитов). Однако, это может быть ресурсозатратно, особенно для форм с большим объемом данных, так как происходит упаковка и распаковка всего контекста формы.
// Пример использования &НаСервере, где на сервер передается весь контекст формы
// Этот код не передает данные явно в параметрах, а работает с реквизитами формы
// (например, "ЭтотОбъект.МояТаблица", которая уже является реквизитом формы)
Процедура ОбновитьДанныеФормыНаСервере() &НаСервере
// В этом контексте мы можем напрямую обращаться к реквизитам формы
// Например, заполнить реквизит формы "ДанныеТабличнойЧасти"
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ИЗ ...";
ЭтотОбъект.ДанныеТабличнойЧасти.Загрузить(Запрос.Выполнить().Выгрузить());
КонецПроцедуры
// На клиенте
Процедура КнопкаОбновитьНажатие(Команда)
ОбновитьДанныеФормыНаСервере();
КонецПроцедуры
При внеконтекстном вызове с аннотацией &НаСервереБезКонтекста на сервер передаются только те данные, которые явно указаны в параметрах процедуры/функции. Это более производительный вариант, так как не происходит передачи всего контекста формы. Мы рекомендуем использовать его в большинстве случаев, если для выполнения серверной операции не требуется весь контекст формы. Этот подход идеален, когда вы просто хотите получить какие-либо данные с сервера без изменения состояния формы.
// Пример использования &НаСервереБезКонтекста для получения данных
Функция ПолучитьКонкретныеДанныеБезКонтекста(ПараметрФильтра) &НаСервереБезКонтекста
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Справочник.Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Родитель = &Родитель";
Запрос.УстановитьПараметр("Родитель", ПараметрФильтра);
Выборка = Запрос.Выполнить().Выбрать();
МассивРезультатов = Новый Массив;
Пока Выборка.Следующий() Цикл
МассивРезультатов.Добавить(Выборка.Наименование);
КонецЦикла;
Возврат МассивРезультатов;
КонецФункции
// На клиенте
Процедура ЗагрузитьДанныеПоФильтруНаКлиенте(Команда)
МойПараметр = СсылкаНаГруппуНоменклатуры; // Например, реквизит формы
СписокИмен = ПолучитьКонкретныеДанныеБезКонтекста(МойПараметр);
Для Каждого Имя Из СписокИмен Цикл
Сообщить(Имя);
КонецЦикла;
КонецПроцедуры
Мы видим, что &НаСервереБезКонтекста – это наш предпочтительный выбор для получения данных, поскольку он минимизирует объем передаваемой информации и повышает производительность.
Для передачи очень больших объемов данных, таких как большие текстовые файлы, двоичные данные, или результаты выгрузки, которые нецелесообразно передавать напрямую через параметры вызова, мы можем использовать временное хранилище. Данные помещаются во временное хранилище на сервере, а затем на клиент передается только адрес этого хранилища. Клиент, используя этот адрес, может получить данные.
Рассмотрим пример, как это работает:
// Код выполняется на сервере
Функция ПоместитьБольшиеДанныеВоВременноеХранилище() &НаСервереБезКонтекста
// Создаем, например, ТаблицуЗначений с очень большим количеством строк
БольшаяТаблицаДанных = Новый ТаблицаЗначений;
БольшаяТаблицаДанных.Колонки.Добавить("Данные");
Для Инд = 1 По 100000 Цикл // 100 000 строк
БольшаяТаблицаДанных.Добавить().Данные = "Строка номер " + Инд;
КонецЦикла;
// Помещаем ТаблицуЗначений во временное хранилище
АдресВХранилище = ПоместитьВоВременноеХранилище(БольшаяТаблицаДанных);
Возврат АдресВХранилище;
КонецФункции
// Код выполняется на клиенте
Процедура ПолучитьДанныеИзХранилища()
АдресДанных = ПоместитьБольшиеДанныеВоВременноеХранилище(); // Получаем адрес с сервера
// Получаем данные из временного хранилища на клиенте по адресу
ПолученныеДанные = ПолучитьИзВременногоХранилища(АдресДанных);
// Теперь ПолученныеДанные - это БольшаяТаблицаДанных, которую можно использовать
Сообщить("Получено " + ПолученныеДанные.Количество() + " строк из временного хранилища.");
КонецПроцедуры
Мы должны помнить, что данные во временном хранилище существуют ограниченное время и доступны только для текущего сеанса. Этот метод особенно полезен для однократной передачи больших объемов.
При работе с передачей данных между клиентом и сервером, мы должны учитывать следующие моменты, чтобы обеспечить стабильность и производительность наших решений:
Оптимизация трафика: Всегда стремитесь передавать с сервера на клиент только те данные, которые действительно необходимы. Избегайте передачи избыточной информации, которая не будет использоваться на клиенте. Каждая лишняя байт данных замедляет работу приложения.
Типы данных: Мы должны помнить, что не все типы данных могут быть сериализованы и переданы между клиентом и сервером напрямую. Например, ссылки на объекты базы данных (СправочникСсылка.Номенклатура) передаются нормально, но такие объекты, как Запрос, ВыборкаИзРезультатаЗапроса, или объекты, не поддерживающие сериализацию, не могут быть переданы напрямую. Всегда проверяйте в синтакс-помощнике, поддерживает ли конкретный объект сериализацию.
Клиент-инициированное взаимодействие: Важно понимать, что взаимодействие между клиентом и сервером всегда происходит по инициативе клиента. Сервер сам по себе не может инициировать передачу информации клиенту. Клиент всегда "запрашивает" данные у сервера.
Асинхронные операции: Для длительных серверных операций, чтобы не блокировать интерфейс пользователя, мы можем использовать механизмы асинхронного выполнения и оповещения о завершении, а также передавать информацию о прогрессе выполнения.
В заключение, мы видим, что передача результатов запроса (Выборка) с сервера на клиент в 1С:Предприятие 8.3 — это стандартная задача, которая эффективно решается путем преобразования серверных объектов в сериализуемые коллекции, такие как Массив Структур или ТаблицаЗначений. Использование внеконтекстных вызовов (&НаСервереБезКонтекста) позволяет нам оптимизировать производительность, а для особо больших объемов данных мы можем применить временное хранилище. Выбирая подходящий метод, мы обеспечиваем надежную и быструю работу наших управляемых форм.