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