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