Почему не получается получить реквизиты по ссылке на клиенте в 1С и как это правильно исправить?

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

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

В чем суть проблемы: разбор исходной ситуации

Рассмотрим ситуацию, с которой часто сталкиваются разработчики. Исходный код, вызывающий ошибку, выглядит следующим образом:


// Вызов на клиенте
Поиск = ЗаполнениеСтрСервер(Элемент.ВыделенныйТекст);

&НаСервере
Функция ЗаполнениеСтрСервер(стр)
    поиск = Справочники.СправочникСоответствия.НайтиПоРеквизиту("Объект", стр);
    Возврат поиск.Ссылка;
КонецФункции

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

Почему на клиенте нельзя получить реквизиты ссылки «через точку»?

В управляемом приложении (тонкий клиент, веб-клиент) действует строгая клиент-серверная модель. База данных физически находится на сервере (или сервере СУБД). Клиентское приложение отвечает исключительно за отображение интерфейса и обработку простейшей логики интерактивного взаимодействия.

Когда серверная функция возвращает на клиент переменную ссылочного типа (например, СправочникСсылка.СправочникСоответствия), происходит следующее:

  1. Клиент получает не сам объект со всеми его реквизитами, а всего лишь его **уникальный идентификатор (GUID)** и закешированное **представление** (текстовую строку, которая чаще всего совпадает с наименованием справочника для вывода на форму).
  2. Вся информация о других реквизитах справочника (таких как дополнительные реквизиты, табличные части, ИНН, коды и т. д.) остается в базе данных на сервере.
  3. Попытка обратиться на клиенте к свойству ссылки напрямую (например, Ссылка.Объект) называется **разыменованием ссылки**. На клиенте эта операция заблокирована платформой на системном уровне.

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

Почему «в другой конфигурации этот же код работает»?

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

  1. Разные режимы запуска приложения (Толстый клиент vs Тонкий клиент). В режиме обычного приложения или в толстом клиенте управляемого приложения разыменование ссылок работает везде, потому что толстый клиент имеет прямой доступ к базе данных и СУБД. В тонком или веб-клиенте этот трюк гарантированно вызовет аварийное завершение работы кода.
  2. Различный контекст исполнения кода. Вполне возможно, что во второй конфигурации обращение к свойствам ссылки происходило внутри метода, выполняющегося на сервере (например, под директивой компиляции &НаСервере или &НаСервереБезКонтекста). На сервере разыменование абсолютно легитимно, поскольку сервер имеет прямой и быстрый доступ к данным.
  3. Путаница в типах данных. Разработчик мог перепутать обычную ссылку с объектом данных формы (например, ДанныеФормыСтруктура), свойства которого доступны на клиенте, если они предварительно были связаны с реквизитами формы.

Анализ ошибок в коде автора темы

Помимо фундаментальной архитектурной ошибки, в исходном коде автора присутствует еще несколько критических недочетов, которые ухудшают производительность и стабильность решения:

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

Рассмотрим несколько профессиональных подходов к решению этой задачи, которые гарантируют высокую скорость работы интерфейса и стабильность кода.

Вариант А. Возврат конкретного значения или структуры (Собственная функция)

Если на клиенте нам нужно получить конкретные реквизиты найденного элемента, мы должны написать внеконтекстную серверную функцию &НаСервереБезКонтекста. Она выполняется быстрее, так как не переносит данные формы по сети. Если требуется один реквизит — возвращаем его значение напрямую. Если нужно несколько — упаковываем их в структуру.

Рассмотрим пример кода реализации данного подхода:


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

&НаСервереБезКонтекста
Функция ПолучитьДанныеСоответствияНаСервере(Знач СтрПоиска)
    
    СсылкаНаОбъект = Справочники.СправочникСоответствия.НайтиПоРеквизиту("Объект", СтрПоиска);
    
    // Обязательная проверка ссылки на заполненность
    Если ЗначениеЗаполнено(СсылкаНаОбъект) Тогда 
        
        // Формируем легкую структуру для отправки на клиент
        Результат = Новый Структура;
        Результат.Вставить("Наименование", СсылкаНаОбъект.Наименование);
        Результат.Вставить("Код", СсылкаНаОбъект.Код);
        
        Возврат Результат;
    КонецЕсли;
    
    // Если объект не найден, возвращаем Неопределено
    Возврат Неопределено;
    
КонецФункции

Вариант Б. Использование встроенных функций БСП

Если вы ведете разработку в типовой конфигурации 1С (например, 1С:Бухгалтерия, 1С:ERP, 1С:УТ), то в системе гарантированно установлена **Библиотека Стандартных Подсистем (БСП)**. В составе БСП есть готовые оптимизированные функции, которые позволяют получать любые реквизиты объектов без написания собственных серверных функций.

Для получения значений реквизитов используются методы общего модуля ОбщегоНазначения:

Посмотрим на пример использования функций БСП в клиентском коде:


&НаКлиенте
Процедура ПолучитьРеквизитЧерезБСП(СсылкаНаСправочник)
    
    Если ЗначениеЗаполнено(СсылкаНаСправочник) Тогда
        // Вызываем функцию БСП напрямую с клиента
        НаименованиеСправочника = ОбщегоНазначенияВызовСервера.ЗначениеРеквизитаОбъекта(СсылкаНаСправочник, "Наименование");
        
        // Используем полученное наименование
        Сообщить("Наименование элемента: " + НаименованиеСправочника);
    КонецЕсли;
    
КонецПроцедуры

Этот способ является наиболее предпочтительным в современной разработке, так как функции БСП оптимизированы на уровне платформы: они используют быстрые SQL-запросы к СУБД, не загружают объект целиком в оперативную память сервера (в отличие от обращения «через точку»), что существенно экономит ресурсы системы.

Резюме по лучшим практикам

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

  1. Никогда не пытайтесь обращаться к свойствам ссылки через точку на клиенте.
  2. Для получения данных всегда пишите методы с директивой &НаСервереБезКонтекста, чтобы минимизировать трафик между клиентом и сервером.
  3. Всегда проверяйте полученные ссылки на заполненность с помощью функции ЗначениеЗаполнено() перед тем, как пытаться прочитать их свойства на сервере.
  4. Активно используйте функции БСП (модули ОбщегоНазначения и ОбщегоНазначенияВызовСервера), чтобы не изобретать велосипеды для чтения реквизитов по ссылке.
← На главную