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