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

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

При разработке универсальных механизмов, подсистем обмена данными или обработчиков событий часто возникает задача выяснить точный тип переданного объекта. В платформе 1С для ссылочных типов существуют удобные обобщенные типы и методы проверки (например, ЛюбаяСсылка). Однако с прикладными объектами манипулирования записями ситуация обстоит иначе. Если попытаться вызвать конструкцию вида Тип("РегистрСведенийМенеджерЗаписи"), платформа вернет Неопределено, так как абстрактного типа менеджера записи без указания конкретного регистра в системе не существует.

Разберем, почему возникает эта сложность, какие ошибки часто допускают разработчики и как правильно и производительно реализовать проверку на принадлежность к объекту РегистрСведенийМенеджерЗаписи.

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

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

  1. Строгая привязка к метаданным: Каждый менеджер записи регистрируется платформой под уникальным типом, включающим имя конкретного регистра, например: РегистрСведенийМенеджерЗаписи.КурсыВалют. Обобщенного системного типа верхнего уровня для таких объектов нет.
  2. Только независимые регистры: Менеджер записи существует исключительно для регистров сведений с независимым режимом записи. Для регистров, подчиненных регистратору, этот тип отсутствует в принципе — для них применяются только наборы записей.
  3. Контекст выполнения: Объект РегистрСведенийМенеджерЗаписи доступен исключительно на стороне сервера, в толстом клиенте и во внешнем соединении. На тонком клиенте передача или создание этого объекта вызовет исключение.
  4. Ненадежность строкового анализа: Попытка использовать код вида СтрНачинаетсяС(Строка(ТипЗнч(ОбъектДанных)), "Регистр сведений менеджер записи") содержит скрытую уязвимость. Функция Строка(ТипЗнч(...)) формирует представление на языке текущего сеанса. В англоязычном интерфейсе или при вызове из веб-сервиса с английской локалью строка примет вид InformationRegisterRecordManager.ИмяРегистра, и условие вернет ложный результат. Кроме того, менеджер записи не сериализуется в XDTO, поэтому метод XMLТипЗнч() для него возвращает Неопределено.

Способ 1. Проверка через поиск метаданных по типу объекта

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

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


Функция ЭтоМенеджерЗаписиРегистраСведений(ЭлементДанных) Экспорт
    
    ТипЗначения = ТипЗнч(ЭлементДанных);
    ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗначения);
    
    Если ОбъектМетаданных <> Неопределено 
        И Метаданные.РегистрыСведений.Содержит(ОбъектМетаданных) Тогда
        
        Возврат ТипЗначения = Тип("РегистрСведенийМенеджерЗаписи." + ОбъектМетаданных.Имя);
    КонецЕсли;
    
    Возврат Ложь;

КонецФункции

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

Способ 2. Оптимизированная проверка через кэширование типов

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

Рассмотрим реализацию инициализации кэша и проверки:


// Функция инициализации кэша (рекомендуется размещать в общем модуле с повторным использованием)
Функция ПолучитьКэшТиповМенеджеровЗаписи() Экспорт

    МассивТипов = Новый Массив;
    
    Для Каждого МДРегистр Из Метаданные.РегистрыСведений Цикл
        // Менеджер записи существует только у независимых регистров
        Если МДРегистр.РежимЗаписи = Метаданные.СвойстваОбъектов.РежимЗаписиРегистраСведений.Независимый Тогда
            МассивТипов.Добавить(Тип("РегистрСведенийМенеджерЗаписи." + МДРегистр.Имя));
        КонецЕсли;
    КонецЦикла;
    
    Возврат Новый ОписаниеТипов(МассивТипов);

КонецФункции

// Быстрая процедура проверки
Функция ЭтоМенеджерЗаписи(ЭлементДанных) Экспорт

    КэшТипов = ПолучитьКэшТиповМенеджеровЗаписи();
    Возврат КэшТипов.СодержитТип(ТипЗнч(ЭлементДанных));

КонецФункции

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

Итоги

Подведем итог: для разовых проверок отлично подходит связка Метаданные.НайтиПоТипу() и динамического сравнения типа через Тип("РегистрСведенийМенеджерЗаписи." + Имя). Для высоконагруженных сценариев правильным выбором будет предварительное кэширование списка типов в ОписаниеТипов с последующим использованием метода СодержитТип().

← На главную