К статье
Практический материал

Разработка и Конфигурация · 11 мин

1С: как получить объект метаданных по ссылке

Как получить объект метаданных по ссылке в 1С: метод ПолучитьОбъект, Метаданные(), НайтиПоСсылке. Примеры кода, проверка битых ссылок, типичные ошибки.

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

В первом случае используется метод ПолучитьОбъект(), во втором — метод ссылки Метаданные() или методы менеджеров объектов вроде Справочники.НайтиПоСсылке(). Ниже разобраны оба сценария с примерами кода, проверкой битых ссылок и ограничениями, о которых нужно помнить при работе в транзакциях и на сервере.

Все примеры приведены на встроенном языке 1С:Предприятие 8.3 и подходят как для управляемых, так и для обычных форм, за исключением моментов, связанных с контекстом клиент/сервер.

Различие между ссылкой, объектом и метаданными

Ссылка — это лёгкий указатель на запись в базе данных. Она содержит уникальный идентификатор и тип, но не содержит самих данных: ни реквизитов, ни табличных частей. Через ссылку можно обратиться к реквизитам «через точку», однако каждое такое обращение — это отдельное обращение к базе, что важно учитывать в циклах.

Объект — это полная копия данных записи, загруженная в память. Только через объект можно изменять реквизиты, табличные части и записывать изменения методом Записать(). Объект получают вызовом Ссылка.ПолучитьОбъект().

Метаданные — это описание структуры: имя объекта конфигурации, состав реквизитов, длина кода, наличие иерархии. Объект описания метаданных по ссылке получают методом Ссылка.Метаданные(). Никаких данных самой записи он не содержит — только структуру вида объекта.

Как получить объект данных по ссылке

Базовый вариант выглядит так:

ОбъектНоменклатуры = СсылкаНаЭлемент.ПолучитьОбъект();

ОбъектНоменклатуры.Наименование = "Новое наименование";

ОбъектНоменклатуры.Записать();

Метод ПолучитьОбъект() выполняет чтение из базы и возвращает объект соответствующего типа (СправочникОбъект, ДокументОбъект и т.д.). Если запись была удалена непосредственно или ссылка «битая», метод вернёт Неопределено — это нужно проверять перед обращением к реквизитам.

Типичная безопасная обёртка:

Если СсылкаНаЭлемент.Пустая() Тогда

Возврат;

КонецЕсли;

ОбъектДанных = СсылкаНаЭлемент.ПолучитьОбъект();

Если ОбъектДанных = Неопределено Тогда

// Запись физически отсутствует в базе

Возврат;

КонецЕсли;

Как определить вид метаданных по ссылке

Если тип ссылки заранее неизвестен (например, реквизит составного типа или универсальная обработка), вид метаданных определяют так:

МетаданныеСсылки = СсылкаНаЭлемент.Метаданные();

ИмяВида = МетаданныеСсылки.Имя; // "Номенклатура"

ПолноеИмя = МетаданныеСсылки.ПолноеИмя(); // "Справочник.Номенклатура"

Синоним = МетаданныеСсылки.Синоним; // "Номенклатура"

Для проверки принадлежности к классу метаданных удобно использовать Метаданные.Справочники.Содержит():

Если Метаданные.Справочники.Содержит(СсылкаНаЭлемент.Метаданные()) Тогда

// Это ссылка на справочник

ИначеЕсли Метаданные.Документы.Содержит(СсылкаНаЭлемент.Метаданные()) Тогда

// Это ссылка на документ

КонецЕсли;

Альтернатива — функция ТипЗнч() совместно с Тип():

Если ТипЗнч(СсылкаНаЭлемент) = Тип("СправочникСсылка.Номенклатура") Тогда

// Конкретный вид справочника

КонецЕсли;

Проверка через ТипЗнч быстрее и читабельнее, когда вид известен заранее. Метод Метаданные() незаменим, когда код должен работать с любым типом ссылки универсально.

Сравнение вариантов

Ссылка.Метаданные()

Универсальный способ для неизвестного типа ссылки; возвращает имя, синоним, полное имя. Не подходит для быстрой проверки конкретного типа в тесном цикле.

ТипЗнч(Ссылка) = Тип(...)

Оптимально, когда вид объекта известен заранее. Короткая и быстрая проверка, но требует перечисления всех ожидаемых типов.

Метаданные.НайтиПоПолномуИмени()

Подходит, когда есть строка вида «Справочник.Номенклатура», например из внешнего источника или настроек обмена.

Обратная задача: ссылка по имени метаданных и идентификатору

Иногда требуется обратное преобразование: имея строковое имя метаданных и уникальный идентификатор, получить ссылку. Для этого используют менеджер объекта:

Менеджер = Справочники[ИмяВида];  // например, Справочники["Номенклатура"]

Ид = Новый УникальныйИдентификатор(СтрокаИд);

СсылкаНаЭлемент = Менеджер.ПолучитьСсылку(Ид);

Для универсального кода по полному имени метаданных менеджер получают через Метаданные.НайтиПоПолномуИмени() и выбор нужной коллекции менеджеров (Справочники, Документы, ПланыСчетов и т.д.).

Проверить, существует ли элемент с таким идентификатором, помогает метод НайтиПоСсылке() у менеджера перечислений либо запрос к базе. Самый надёжный способ убедиться, что ссылка не «битая», — запрос:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ ПЕРВЫЕ 1 Т.Ссылка

|ИЗ Справочник.Номенклатура КАК Т

|ГДЕ Т.Ссылка = &Ссылка";

Запрос.УстановитьПараметр("Ссылка", СсылкаНаЭлемент);

Если Запрос.Выполнить().Пустой() Тогда

// Ссылка битая — записи в базе нет

КонецЕсли;

Порядок действий при универсальной обработке ссылки
  1. 1
    Проверьте ссылку на пустоту: Ссылка.Пустая()
  2. 2
    Определите вид: Ссылка.Метаданные().ПолноеИмя()
  3. 3
    Проверьте существование записи запросом или через ПолучитьОбъект() с проверкой на Неопределено
  4. 4
    Получите объект через ПолучитьОбъект(), если нужны изменения
  5. 5
    Измените данные и вызовите Записать()
  6. 6
    Проверьте результат повторным чтением или запросом

Особенности контекста: клиент, сервер, транзакция

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

Объект, полученный через ПолучитьОбъект(), нельзя передать на клиент в управляемых формах — он не сериализуется. Для передачи данных на форму используют реквизиты формы, значения реквизитов объекта через ЗначениеВРеквизитФормы() либо структуры с нужными полями.

При записи объекта внутри явной транзакции помните про блокировки: параллельные сеансы, изменяющие тот же элемент, вызовут конфликт блокировок. В массовых обработках разумно обрабатывать исключения и повторять попытку или пропускать элемент с записью в журнал.

Типичные ошибки и их последствия

Первая распространённая ошибка — обращение к реквизитам результата ПолучитьОбъект() без проверки на Неопределено. Как только в обрабатываемых данных встретится битая ссылка, обработка упадёт с ошибкой, а часть данных останется необработанной.

Вторая ошибка — сравнение типа через строковое представление ссылки (Строка(Ссылка)) или через XMLТип там, где достаточно ТипЗнч(). Это медленнее и хрупче при переименовании объектов конфигурации.

Третья ошибка — попытка получить Метаданные() у значения, которое не является ссылкой, например у объекта или у Неопределено. Перед вызовом убедитесь, что значение — именно ссылка: для этого удобна проверка через Метаданные.НайтиПоТипу(ТипЗнч(Значение)) — она вернёт Неопределено для примитивных типов.

Типичные ошибки
01
Вызов ПолучитьОбъект() без проверки на Неопределено — падение обработки на битых ссылках
02
Чтение реквизитов через точку от ссылки в большом цикле — резкое падение производительности
03
Попытка вызвать ПолучитьОбъект() на клиенте в управляемом приложении — ошибка вызова метода
04
Запись объекта без ОбменДанными.Загрузка при загрузке из обмена — срабатывание всех проверок и подписок, лишние движения
05
Сравнение типов по строковому представлению — код ломается при переименовании метаданных

Проверка результата и контрольные точки

После записи объекта убедитесь, что изменения действительно сохранены: повторно прочитайте данные запросом или откройте элемент в пользовательском режиме. Если запись выполнялась в транзакции, проверка имеет смысл только после её фиксации.

Признак корректной работы кода определения метаданных — метод ПолноеИмя() возвращает строку вида «Справочник.Номенклатура» или «Документ.РеализацияТоваровУслуг». Если возвращается пустая строка или ошибка — скорее всего, на входе не ссылка, а объект или примитивное значение.

Контрольные точки

Как проверить результат

Ссылка.Метаданные().ПолноеИмя()
Норма

Строка вида «Справочник.Номенклатура»

Тревожный признак

Ошибка вызова метода — значение не является ссылкой

Результат ПолучитьОбъект()
Норма

Объект соответствующего типа, реквизиты читаются

Тревожный признак

Неопределено — запись удалена или ссылка битая

Запрос по ссылке
Норма

Одна строка в результате

Тревожный признак

Пустой результат — записи в базе нет

После Записать()
Норма

Повторное чтение показывает новые значения

Тревожный признак

Старые значения — запись не выполнилась или откачена транзакция

При отладке универсальных обработок полезно выводить в журнал регистрации полное имя метаданных и строковое представление ссылки — это существенно ускоряет поиск проблемных записей при разборе ошибок пользователей.

Итог

Что важно запомнить

Ссылка.ПолучитьОбъект() — для чтения и изменения данных записи, Ссылка.Метаданные() — для определения вида объекта конфигурации. Всегда проверяйте ссылку на пустоту и результат на Неопределено, а массовое чтение выполняйте запросами, а не ПолучитьОбъект() в цикле.

Часто задаваемые вопросы

Чем ссылка отличается от объекта в 1С?

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

Можно ли получить метаданные по строковому имени, например «Справочник.Номенклатура»?

Да, используйте Метаданные.НайтиПоПолномуИмени("Справочник.Номенклатура"). Метод вернёт объект описания метаданных или Неопределено, если объекта с таким именем в конфигурации нет. Это удобно при разборе настроек обменов и внешних описаний типов.

Как проверить, что ссылка «битая»?

Надёжнее всего — запросом к таблице объекта с условием по ссылке: пустой результат означает, что записи в базе нет. Вариант без запроса — вызвать ПолучитьОбъект() и сравнить результат с Неопределено, но это тяжелее по ресурсам при массовых проверках.

Почему ПолучитьОбъект() вызывает ошибку в управляемой форме?

Метод доступен только на сервере. Если процедура выполняется в клиентском контексте управляемого приложения, вызов приведёт к ошибке. Перенесите код в серверную процедуру общего модуля с включённым признаком «Сервер» и «Вызов сервера».

Как определить тип ссылки, если заранее неизвестно, справочник это или документ?

Вызовите Ссылка.Метаданные() и проверьте принадлежность через Метаданные.Справочники.Содержит(), Метаданные.Документы.Содержит() и аналогичные коллекции. Полное имя вида вернёт метод ПолноеИмя() — например, «Документ.ПоступлениеТоваров».

Безопасно ли массово изменять объекты через ПолучитьОбъект() и Записать()?

Технически да, но с оговорками: делайте резервную копию, обрабатывайте исключения, учитывайте блокировки параллельных сеансов и по возможности используйте ОбменДанными.Загрузка = Истина только тогда, когда осознанно пропускаете бизнес-проверки. Для теста сначала прогоните обработку на копии базы.