Почему нельзя вызвать серверную процедуру из клиентской внутри глобального общего модуля?

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

В процессе разработки на платформе «1С:Предприятие 8» многие программисты сталкиваются с ситуацией, когда процедура, определенная в глобальном общем модуле с директивой #Если Сервер Тогда, оказывается недоступна для вызова из клиентской процедуры того же самого модуля. При этом флаг «Вызов сервера» в свойствах модуля установлен, а сама процедура помечена как Экспорт. Давайте подробно разберем, почему платформа ведет себя именно так, и как правильно организовать архитектуру решения задачи.

Разберем механизм компиляции контекстов

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

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

Рассмотрим ситуацию по шагам:

  1. При запуске клиента компилируется клиентская часть глобального модуля. В нее попадают только те методы, которые не ограничены инструкциями препроцессора или помечены как #Если Клиент Тогда.
  2. Методы, обернутые в #Если Сервер Тогда, в клиентскую версию модуля просто не попадают. Для клиента их не существует.
  3. Когда из клиентской процедуры происходит вызов серверного метода, платформа ищет его в глобальном контексте клиента. Не найдя его там, она выдает ошибку «Процедура или функция с указанным именем не определена».

Выясним причину игнорирования флага «Вызов сервера»

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

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

Проанализируем пример некорректной структуры кода в глобальном модуле (назовем его ГлобальныйМодуль, установлены галки Клиент, Сервер, Вызов сервера):


Процедура КлиентскийМетод() Экспорт
    // Платформа попытается найти СерверныйМетод() в глобальном контексте клиента.
    // Ошибка: метод не найден, так как Вызов сервера для глобальных модулей не работает.
    СерверныйМетод(); 
КонецПроцедуры

#Если Сервер Тогда
Процедура СерверныйМетод() Экспорт
    // Какая-то логика на сервере
КонецПроцедуры
#КонецЕсли

Как правильно реализовать задачу (Best Practices)

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

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

  1. Создаем глобальный клиентский модуль (например, ОповещенияКлиентГлобальный).
    • Флаги: Клиент (управляемое приложение), Глобальный.
    • Флаги «Сервер» и «Вызов сервера» должны быть выключены.
    • В нем располагаем только клиентскую логику и процедуру-обработчик.
  2. Создаем обычный серверный модуль (например, ОповещенияСервер).
    • Флаги: Сервер, Вызов сервера.
    • Флаг «Глобальный» должен быть выключен.
    • В нем располагаем экспортные процедуры, выполняющие запросы к базе данных.

Посмотрим на пример кода, который реализует эту схему:

В модуле ОповещенияКлиентГлобальный:


Процедура ОбработчикПроверкиОповещений() Экспорт
    // Вызываем серверный метод НЕглобального модуля
    // Здесь механизм "Вызов сервера" отработает штатно
    ДанныеДляПользователя = ОповещенияСервер.ПолучитьНовыеСообщения();
    
    Если ДанныеДляПользователя.Количество() > 0 Тогда
        // Логика отображения оповещения пользователю
    КонецЕсли;
КонецПроцедуры

В модуле ОповещенияСервер:


Функция ПолучитьНовыеСообщения() Экспорт
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ ..."; // Ваша логика запроса
    Возврат Запрос.Выполнить().Выгрузить();
КонецФункции

Особенности работы в Толстом клиенте

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

Резюме

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

← На главную