Как получить ссылки на элементы справочников из другой базы через COM-соединение?

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

При работе с распределенными информационными базами (РИБ) или при интеграции двух различных баз 1С часто возникает задача: получить данные из внешней базы так, чтобы они стали "родными" ссылками в текущей базе. Основная сложность заключается в том, что при использовании COM-соединения (V83.COMConnector) любые полученные ссылки на справочники или документы приходят в виде типа COMОбъект. Текущая база "не узнает" их, и мы не можем напрямую записать такой объект в реквизит или использовать его в запросе.

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

Суть проблемы: почему COM-объект — это не ссылка

Когда мы выполняем запрос через COM-соединение, например, к регистру сведений во второй базе, результат запроса содержит объекты, которые живут в адресном пространстве другого процесса. Даже если в обеих базах справочник Контрагенты абсолютно идентичен (одинаковые коды, наименования и GUID), для платформы ссылка из COM-соединения — это лишь указатель на структуру данных во внешнем источнике.

Чтобы "превратить" этот указатель в ссылку текущей базы, нам нужно найти "мостик". В РИБ таким мостиком является УникальныйИдентификатор (UUID). Если объекты были синхронизированы, их GUID совпадают. Наша задача — извлечь GUID из COM-базы и найти объект с таким же GUID в нашей базе.

Способ 1: Получение ссылки через строковое представление GUID

Это классический и самый надежный метод. Проанализируем пошагово, как это реализовать в коде. Основная идея заключается в использовании метода УникальныйИдентификатор() у COM-объекта и его последующей конвертации.

Рассмотрим пример, где мы подключаемся к внешней базе и читаем данные регистра сведений:


// 1. Устанавливаем соединение
ПараметрыСвязи = "Srvr=""ServerName"";Ref=""BaseName"";Usr=""Admin"";Pwd=""Password"";";
Connector = Новый COMОбъект("V83.COMConnector");
Попытка
    ВнешняяБаза = Connector.Connect(ПараметрыСвязи);
Исключение
    Сообщить("Не удалось подключиться: " + ОписаниеОшибки());
    Возврат;
КонецПопытки;

// 2. Выполняем запрос во внешней базе
Запрос = ВнешняяБаза.NewObject("Запрос");
Запрос.Текст = "ВЫБРАТЬ Организация, Сумма ИЗ РегистрСведений.МойРегистр";
Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
    // Здесь Выборка.Организация — это COMОбъект
    
    // 3. Извлекаем строковый GUID. 
    // Важно использовать метод XMLСтрока самого COM-соединения!
    СтрGUID = ВнешняяБаза.XMLСтрока(Выборка.Организация.УникальныйИдентификатор());
    
    // 4. Создаем GUID в нашей базе и получаем ссылку
    НовыйGUID = Новый УникальныйИдентификатор(СтрGUID);
    СсылкаЛокальная = Справочники.Организации.ПолучитьСсылку(НовыйGUID);
    
    // Теперь СсылкаЛокальная — это полноценная ссылка текущей базы
КонецЦикла;

Важный нюанс: почему мы используем ВнешняяБаза.XMLСтрока(...)? Дело в том, что метод УникальныйИдентификатор() возвращает тоже COM-объект. Если мы попытаемся вызвать String() в локальной базе, результат может зависеть от региональных настроек. Метод XMLСтрока гарантирует стандартный формат GUID (типа "550e8400-e29b-41d4-a716-446655440000"), который безошибочно понимает конструктор Новый УникальныйИдентификатор().

Способ 2: Оптимизация через выгрузку в таблицу значений

Если записей в регистре много (тысячи или десятки тысяч), обращение к каждому свойству Выборка.Организация через точку в цикле будет работать крайне медленно. Каждое такое обращение — это отдельный вызов между процессами (RPC-вызов), что создает огромные накладные расходы.

Проанализируем ситуацию: как ускорить процесс? Правильный путь — получить все данные одним "пакетом". Мы можем подготовить таблицу значений на стороне COM-базы, где GUID уже будет представлен в виде строки, и передать эту таблицу целиком.

Рассмотрим улучшенный пример кода:


ТекстЗапроса = 
"ВЫБРАТЬ 
|   МойРегистр.Организация КАК СсылкаОрг, 
|   МойРегистр.Сумма КАК Сумма 
|ИЗ 
|   РегистрСведений.МойРегистр КАК МойРегистр";

ЗапросВнешний = ВнешняяБаза.NewObject("Запрос");
ЗапросВнешний.Текст = ТекстЗапроса;

// Выгружаем результат в таблицу значений на стороне COM
ТЗВнешняя = ЗапросВнешний.Выполнить().Выгрузить();

// Добавляем колонку для строкового представления идентификатора
ТЗВнешняя.Колонки.Добавить("СтрGUID", ВнешняяБаза.NewObject("ОписаниеТипов", "Строка"));

// Проходим по таблице в COM-базе и заполняем строковые идентификаторы
// Это все еще медленно, но мы делаем это ОДИН раз
Для Каждого Стр Из ТЗВнешняя Цикл
    Стр.СтрGUID = ВнешняяБаза.XMLСтрока(Стр.СсылкаОрг.УникальныйИдентификатор());
КонецЦикла;

// Теперь переносим данные в нашу локальную ТЗ
МояТЗ = Новый ТаблицаЗначений;
МояТЗ.Колонки.Добавить("Организация", Новый ОписаниеТипов("СправочникСсылка.Организации"));
МояТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));

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

Проблема "битых" ссылок

Нужно учитывать важную особенность метода ПолучитьСсылку(). Этот метод работает "вслепую": он создает ссылку в памяти на основе переданного GUID, но не проверяет, существует ли такой объект в базе данных на самом деле. Если в базе-приемнике объект еще не был создан или был удален, вы получите так называемую "битую ссылку" (Объект не найден).

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


Если СсылкаЛокальная.ПолучитьОбъект() = Неопределено Тогда
    // Объекта с таким GUID в нашей базе нет
    // Здесь можно либо пропустить запись, либо вывести предупреждение
КонецЕсли;

В условиях РИБ объекты обычно идентичны, но рассинхронизация может случиться из-за настроек фильтрации обмена или ошибок передачи данных, поэтому такая проверка никогда не будет лишней.

Работа с составными типами данных

Ситуация усложняется, если поле в регистре имеет составной тип (например, реквизит Владелец может быть и СправочникСсылка.Контрагенты, и СправочникСсылка.ФизическиеЛица). В этом случае нам нужно сначала понять, к какому метаданному относится COM-объект.

Разберем, как определить тип объекта через COM:

  1. Используйте метод ВнешняяБаза.XMLТипЗнч(Выборка.Поле). Он вернет структуру с именем типа и пространством имен.
  2. На основе полученного имени типа принимайте решение, к какому менеджеру справочников обращаться (Справочники.Контрагенты или Справочники.ФизическиеЛица).

Пример получения имени метаданных:

ИмяМетаданных = Выборка.Поле.Метаданные().Имя;

Подведем итоги

Процесс получения реальных ссылок из COM-соединения сводится к трем этапам: извлечение GUID во внешней базе -> передача его строкового представления -> генерация ссылки в текущей базе по этому идентификатору.

Основные рекомендации:

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

← На главную