Работа с метаданными через COM-соединение — это задача, с которой часто сталкиваются разработчики при организации обмена данными или создании универсальных инструментов мониторинга систем 1С. Основная сложность заключается в том, что объекты, передаваемые через COM-границу, ведут себя иначе, чем в родном контексте базы данных. Например, привычные функции могут возвращать неочевидный результат «COMОбъект» вместо ожидаемого имени типа. В этой статье мы подробно разберем, как эффективно определять типы реквизитов, работать с составными типами и оптимизировать этот процесс.
Когда мы пытаемся получить строковое описание типа через COM, используя стандартную функцию Строка(ТипРеквизита), вызывающая база часто не может корректно интерпретировать объект и возвращает универсальное системное имя COMОбъект. Это происходит потому, что объект Тип принадлежит контексту внешней базы, а локальная база «не знает», как его визуализировать.
Для решения этой проблемы мы должны заставить внешнюю базу саму преобразовать свой тип в строку. Рассмотрим, как это сделать правильно:
// Предположим, Соединение — это ваш установленный COM-объект
ТипРеквизита = РеквизитМетаданных.Тип.Типы()[0];
ИмяТипа = Соединение.String(ТипРеквизита);
Сообщить("Тип реквизита: " + ИмяТипа);
Метод String(), вызванный через объект соединения, принудительно выполняет преобразование на стороне источника, возвращая нам понятную строку, например, «СправочникСсылка.Номенклатура».
Проанализируем наиболее распространенный способ — поиск объекта метаданных по его типу. Это позволяет получить не просто строку, а полное программное имя (FullName), которое можно использовать для дальнейших конструкций в коде. Разберем этот алгоритм по шагам:
ОписаниеТипов реквизита.НайтиПоТипу().
Для Каждого ТекМетаданныеСправочника Из Соединение.Метаданные.Справочники Цикл
КоллекцияРеквизитов = ТекМетаданныеСправочника.Реквизиты;
Для Каждого Реквизит Из КоллекцияРеквизитов Цикл
// Работаем с описанием типов
ОписаниеТипов = Реквизит.Тип;
МассивТипов = ОписаниеТипов.Типы();
Для Каждого ТекущийТип Из МассивТипов Цикл
// Ищем объект метаданных во внешней базе
ОбъектМетаданныхТипа = Соединение.Метаданные.НайтиПоТипу(ТекущийТип);
Если ОбъектМетаданныхТипа <> Неопределено Тогда
ПолноеИмя = ОбъектМетаданныхТипа.ПолноеИмя();
Сообщить("Реквизит: " + Реквизит.Имя + " имеет тип: " + ПолноеИмя);
Иначе
// Если НайтиПоТипу вернул Неопределено, возможно это примитивный тип
Сообщить("Реквизит: " + Реквизит.Имя + " имеет примитивный тип: " + Соединение.String(ТекущийТип));
КонецЕсли;
КонецЦикла;
КонецЦикла;
КонецЦикла;
Как мы выяснили выше, метод НайтиПоТипу() возвращает Неопределено для простых типов (Число, Строка, Дата, Булево). Чтобы точно идентифицировать их, мы можем сравнить тип реквизита с эталонными типами внешней базы. Рассмотрим, как программно получить эталон типа через COM:
ТипСтрока = Соединение.NewObject("ОписаниеТипов", "String").Типы()[0];
ТипЧисло = Соединение.NewObject("ОписаниеТипов", "Number").Типы()[0];
Если ТекущийТип = ТипСтрока Тогда
// Это строка
КонецЕсли;
Важный момент: попытка использовать локальную функцию ТипЗнч() внутри вызывающей базы для объектов из COM приведет к ошибке «Метод не обнаружен», так как это глобальная функция контекста, а не метод объекта. Всегда используйте методы через переменную соединения.
Существует альтернативный, хотя и менее производительный способ определения типа через создание элемента. Если нам нужно узнать тип конкретного поля уже существующего объекта, мы можем обратиться к метаданным через созданный (но не записанный) экземпляр. Выясним, как это выглядит на практике:
// Пример получения типа для реквизита справочника Номенклатура
НовыйЭлемент = Соединение.Справочники.Номенклатура.СоздатьЭлемент();
ИмяТипа = НовыйЭлемент.ЕдиницаИзмерения.Метаданные().ПолноеИмя();
Этот метод удобен тем, что 1С автоматически возвращает метаданные значения. Однако помните, что в случае составного типа значение реквизита в новом объекте будет равно Неопределено, и вызов Метаданные() приведет к ошибке.
Проанализируем более надежный технический способ, который не зависит от языка интерфейса или особенностей представления метаданных — использование XDTO. Метод XMLТип позволяет получить пространство имен и имя типа, которые стандартизированы в платформе.
// Получаем тип через сериализатор XDTO внешней базы
Сериализатор = Соединение.СериализаторXDTO;
ТипXML = Сериализатор.XMLТип(ТекущийТип);
Если ТипXML <> Неопределено Тогда
Сообщить("Локальное имя XML: " + ТипXML.ИмяТипа);
Сообщить("URI пространства имен: " + ТипXML.URIПространстваИмен);
КонецЕсли;
Этот способ незаменим при создании сложных интеграционных решений, где требуется стопроцентная точность сопоставления объектов между разными конфигурациями.
Выясним важную деталь: каждое обращение через точку в COM-соединении (например, Соединение.Метаданные.Справочники...) — это отдельный сетевой вызов (если базы на разных серверах) и серьезные затраты ресурсов на маршаллинг объектов. Если у вас сотни справочников и тысячи реквизитов, перебор их в цикле через COM будет работать крайне медленно.
Рекомендация по ускорению:
ТаблицуЗначений или Структуру на своей стороне.Такой подход сокращает количество COM-запросов с тысяч до одного, что ускоряет получение структуры метаданных в десятки раз. Рассмотрим пример логики такой функции:
// На стороне внешней базы (в общем модуле)
Функция ПолучитьСтруктуруМетаданныхВJSON() Экспорт
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Объект");
Таблица.Колонки.Добавить("Реквизит");
Таблица.Колонки.Добавить("Тип");
// ... логика обхода метаданных и заполнения таблицы ...
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Таблица);
Возврат ЗаписьJSON.Закрыть();
КонецФункции
Использование вышеперечисленных методов позволит вам уверенно определять типы любых реквизитов при работе через COM-соединение, корректно обрабатывать составные типы данных и обеспечивать высокую производительность ваших интеграционных решений.