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