Как программно получить типы метаданных и быстро найти объект по GUID в 1С

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

В процессе разработки универсальных инструментов, выгрузок или обработок администрирования перед программистом 1С часто возникают две взаимосвязанные задачи. Первая — получить полный список доступных типов данных (аналогично подсказке в конфигураторе при вводе конструкции Тип("...")). Вторая — выполнить универсальный поиск существующего объекта в базе данных, зная только его уникальный идентификатор (GUID) и не имея информации о его типе.

В этой статье мы подробно рассмотрим, как программно обходить метаданные для построения списка типов, разберем базовый алгоритм поиска по GUID, проанализируем его слабые стороны с точки зрения производительности и предложим высокоэффективные варианты оптимизации, включая использование механизмов БСП и динамическую сборку пакетных запросов.

Часть 1. Программное получение имен типов метаданных

При вводе строки в конфигураторе внутри конструкции Тип("...") платформа выводит интерактивную подсказку. Строковое представление типа для ссылочных объектов метаданных в 1С всегда формируется по строгому системному шаблону: [ИмяКлассаМетаданных]Ссылка.[ИмяОбъекта] (например, СправочникСсылка.Номенклатура или ДокументСсылка.ЗаказКлиента).

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


СписокТипов = Новый Массив;

// Определим структуру классов метаданных, которые имеют ссылочный тип
КлассыСсылок = Новый Структура;
КлассыСсылок.Вставить("Справочники", "СправочникСсылка");
КлассыСсылок.Вставить("Документы", "ДокументСсылка");
КлассыСсылок.Вставить("ПланыВидовХарактеристик", "ПланВидовХарактеристикСсылка");
КлассыСсылок.Вставить("ПланыСчетов", "ПланСчетовСсылка");
КлассыСсылок.Вставить("ПланыОбмена", "ПланОбменаСсылка");
КлассыСсылок.Вставить("Задачи", "ЗадачаСсылка");
КлассыСсылок.Вставить("БизнесПроцессы", "БизнесПроцессСсылка");

// Обходим коллекции метаданных циклом
Для Каждого Класс Из КлассыСсылок Цикл
    ИмяКоллекции = Класс.Ключ; // Например, "Справочники"
    ПрефиксТипа  = Класс.Значение; // Например, "СправочникСсылка"
    
    Для Каждого ОбъектМетаданных Из Метаданные[ИмяКоллекции] Цикл
        СтрокаТипа = ПрефиксТипа + "." + ОбъектМетаданных.Имя;
        СписокТипов.Добавить(СтрокаТипа);
    EndDo;
EndDo;

Согласно стандартам разработки фирмы «1С», для оптимальной производительности необходимо соблюдать следующие правила:

  1. Если тип конкретного объекта или ссылки уже известен в коде, его метаданные следует получать напрямую через свойство объекта, например: Ссылка.Метаданные(). Это работает в разы быстрее, чем поиск через глобальный контекст.
  2. Если у вас на руках есть только значение неизвестного типа, используйте метод Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)).

Часть 2. Базовый алгоритм поиска объекта по GUID

Рассмотрим классическую задачу: у нас есть текстовое представление уникального идентификатора (GUID), и нам необходимо найти, какому именно объекту в базе данных он принадлежит. Тип объекта (справочник, документ или план счетов) нам неизвестен.

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


&НаСервере
Процедура ПоискОбъектаПоGUIDНаСервере()
    
    УИ = Новый УникальныйИдентификатор(Объект.GUID);
    НайденнаяСсылка = Неопределено;
    
    // Последовательно опрашиваем менеджеры основных ссылочных классов
    Если ПолучитьСсылкуПоМенеджеруОбъекта(Справочники, УИ, НайденнаяСсылка) Тогда
        Объект.НайденнаяСсылка = НайденнаяСсылка;
    ИначеЕсли ПолучитьСсылкуПоМенеджеруОбъекта(Документы, УИ, НайденнаяСсылка) Тогда
        Объект.НайденнаяСсылка = НайденнаяСсылка;
    ИначеЕсли ПолучитьСсылкуПоМенеджеруОбъекта(ПланыВидовХарактеристик, УИ, НайденнаяСсылка) Тогда
        Объект.НайденнаяСсылка = НайденнаяСсылка;
    ИначеЕсли ПолучитьСсылкуПоМенеджеруОбъекта(ПланыСчетов, УИ, НайденнаяСсылка) Тогда
        Объект.НайденнаяСсылка = НайденнаяСсылка;
    ИначеЕсли ПолучитьСсылкуПоМенеджеруОбъекта(ПланыОбмена, УИ, НайденнаяСсылка) Тогда
        Объект.НайденнаяСсылка = НайденнаяСсылка;
    ИначеЕсли ПолучитьСсылкуПоМенеджеруОбъекта(БизнесПроцессы, УИ, НайденнаяСсылка) Тогда
        Объект.НайденнаяСсылка = НайденнаяСсылка;
    ИначеЕсли ПолучитьСсылкуПоМенеджеруОбъекта(Задачи, УИ, НайденнаяСсылка) Тогда
        Объект.НайденнаяСсылка = НайденнаяСсылка;
    Иначе
        Сообщить("Объект с таким GUID в базе данных не найден.");
    КонецЕсли;
    
КонецПроцедуры

&НаСервере
Функция ПолучитьСсылкуПоМенеджеруОбъекта(ОбъектыМенеджер, УИ, НайденнаяСсылка)
    
    Для Каждого Менеджер Из ОбъектыМенеджер Цикл
        // Создаем ссылку в оперативной памяти без обращения к СУБД
        ПроверяемаяСсылка = Менеджер.ПолучитьСсылку(УИ);
        
        // Пытаемся получить объект. Если он существует - возвращается объект, если нет - Неопределено
        Если ПроверяемаяСсылка.ПолучитьОбъект() <> Неопределено Тогда
            НайденнаяСсылка = ПроверяемаяСсылка;
            Возврат Истина;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Ложь;
    
КонецФункции

Анализ проблемы производительности базового решения

Несмотря на то, что приведенный выше код полностью работоспособен, он обладает критическим недостатком производительности.

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

Если ваша конфигурация содержит сотни справочников и документов (например, как в ERP, УТ или КА), выполнение этого кода приведет к генерации сотен тяжелых SQL-запросов. Это вызовет существенное зависание серверного вызова и создаст колоссальную, неоправданную нагрузку на сервер баз данных.


Часть 3. Оптимизация поиска по GUID

Для ускорения работы этой операции мы можем применить два альтернативных профессиональных подхода.

Вариант №1. Использование функций БСП (Библиотека стандартных подсистем)

Если ваше решение развернуто на любой современной типовой конфигурации 1С, в ней гарантированно присутствует БСП. В общем модуле ОбщегоНазначения предусмотрена готовая функция СсылкаСуществует().

Вместо тяжелого метода ПолучитьОбъект() мы можем переписать проверку существования записи в базе данных следующим образом:


// Вместо: Если ПроверяемаяСсылка.ПолучитьОбъект() <> Неопределено
Если ОбщегоНазначения.СсылкаСуществует(ПроверяемаяСсылка) Тогда
    НайденнаяСсылка = ПроверяемаяСсылка;
    Возврат Истина;
КонецЕсли;

Внутри этой функции БСП выполняет быстрый и легкий запрос вида: ВЫБРАТЬ Ссылка ИЗ [Таблица] ГДЕ Ссылка = &Ссылка. Это защищает СУБД от избыточного чтения тяжелых полей и ускоряет поиск в десятки раз.

Вариант №2. Формирование динамического пакетного запроса (Самый быстрый способ)

Максимальную скорость поиска можно получить, если минимизировать количество обращений к СУБД до одного-единственного запроса. Мы можем динамически обойти все метаданные в памяти сервера приложений, сгенерировать единый текст запроса, объединяющий выборки из всех таблиц через оператор ОБЪЕДИНИТЬ ВСЕ, и выполнить его за один раз.

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


&НаСервере
Функция НайтиСсылкуПоGUIDЧерезЗапрос(УИД) Экспорт
    
    Запрос = Новый Запрос;
    ТекстЗапроса = "";
    
    // Задаем массив классов метаданных для поиска
    МассивКлассов = Новый Массив;
    МассивКлассов.Добавить(Метаданные.Справочники);
    МассивКлассов.Добавить(Метаданные.Документы);
    МассивКлассов.Добавить(Метаданные.ПланыВидовХарактеристик);
    МассивКлассов.Добавить(Метаданные.ПланыСчетов);
    МассивКлассов.Добавить(Метаданные.ПланыОбмена);
    МассивКлассов.Добавить(Метаданные.Задачи);
    МассивКлассов.Добавить(Метаданные.БизнесПроцессы);
    
    Для Каждого Класс Из МассивКлассов Цикл
        Для Каждого ОбъектМетаданных Из Класс Цикл
            
            ИмяТаблицы = ОбъектМетаданных.ПолноеИмя();
            ИмяПараметра = "Параметр_" + СтрЗаменить(ИмяТаблицы, ".", "_");
            
            Если ТекстЗапроса <> "" Тогда
                ТекстЗапроса = ТекстЗапроса + Символы.ПС + "ОБЪЕДИНИТЬ ВСЕ" + Символы.ПС;
            КонецЕсли;
            
            // Формируем легкий точечный запрос к таблице
            ТекстЗапроса = ТекстЗапроса + "ВЫБРАТЬ Ссылка КАК Ссылка ИЗ " + ИмяТаблицы + " ГДЕ Ссылка = &" + ИмяПараметра;
            
            // Получаем ссылку в памяти через динамическое обращение к менеджеру
            ИмяКласса = ОбъектМетаданных.Родитель().Имя; // Например, "Справочники"
            ПроверяемаяСсылка = [ИмяКласса][ОбъектМетаданных.Имя].ПолучитьСсылку(УИД);
            
            Запрос.УстановитьПараметр(ИмяПараметра, ПроверяемаяСсылка);
            
        КонецЦикла;
    КонецЦикла;
    
    Запрос.Текст = ТекстЗапроса;
    Результат = Запрос.Выполнить();
    
    Если Не Результат.Пустой() Тогда
        Выборка = Результат.Выбрать();
        Выборка.Следующий();
        Возврат Выборка.Ссылка;
    КонецЕсли;
    
    Возврат Неопределено;
    
КонецФункции

Этот способ отрабатывает мгновенно, поскольку СУБД выполняет поиск по индексу первичного ключа (кластеризованному индексу Ссылка) сразу по всей базе данных за одно транзакционное обращение.


Часть 4. Определение типа по «битой» ссылке (Альтернативный сценарий)

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

«<Объект не найден> (84:bf5600145e3710ab11dda4c605dbe824)»

Если у вас есть строковое представление такой неразрешенной («битой») ссылки, то сканировать всю базу данных и перебирать метаданные вообще не требуется!

Внутренний формат представления ненайденного объекта в платформе 1С уже содержит всю исчерпывающую информацию для точного восстановления типа:

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

← На главную