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