При работе с распределенными информационными базами (РИБ) или при интеграции двух различных баз 1С часто возникает задача: получить данные из внешней базы так, чтобы они стали "родными" ссылками в текущей базе. Основная сложность заключается в том, что при использовании COM-соединения (V83.COMConnector) любые полученные ссылки на справочники или документы приходят в виде типа COMОбъект. Текущая база "не узнает" их, и мы не можем напрямую записать такой объект в реквизит или использовать его в запросе.
В этой статье мы подробно разберем, как преобразовать эти внешние прокси-объекты в реальные ссылки 1С, основываясь на идентичности их уникальных идентификаторов (GUID), и рассмотрим способы оптимизации этого процесса.
Когда мы выполняем запрос через COM-соединение, например, к регистру сведений во второй базе, результат запроса содержит объекты, которые живут в адресном пространстве другого процесса. Даже если в обеих базах справочник Контрагенты абсолютно идентичен (одинаковые коды, наименования и GUID), для платформы ссылка из COM-соединения — это лишь указатель на структуру данных во внешнем источнике.
Чтобы "превратить" этот указатель в ссылку текущей базы, нам нужно найти "мостик". В РИБ таким мостиком является УникальныйИдентификатор (UUID). Если объекты были синхронизированы, их GUID совпадают. Наша задача — извлечь GUID из COM-базы и найти объект с таким же 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"), который безошибочно понимает конструктор Новый УникальныйИдентификатор().
Если записей в регистре много (тысячи или десятки тысяч), обращение к каждому свойству Выборка.Организация через точку в цикле будет работать крайне медленно. Каждое такое обращение — это отдельный вызов между процессами (RPC-вызов), что создает огромные накладные расходы.
Проанализируем ситуацию: как ускорить процесс? Правильный путь — получить все данные одним "пакетом". Мы можем подготовить таблицу значений на стороне COM-базы, где GUID уже будет представлен в виде строки, и передать эту таблицу целиком.
Рассмотрим улучшенный пример кода:
ТекстЗапроса =
"ВЫБРАТЬ
| МойРегистр.Организация КАК СсылкаОрг,
| МойРегистр.Сумма КАК Сумма
|ИЗ
| РегистрСведений.МойРегистр КАК МойРегистр";
ЗапросВнешний = ВнешняяБаза.NewObject("Запрос");
ЗапросВнешний.Текст = ТекстЗапроса;
// Выгружаем результат в таблицу значений на стороне COM
ТЗВнешняя = ЗапросВнешний.Выполнить().Выгрузить();
// Добавляем колонку для строкового представления идентификатора
ТЗВнешняя.Колонки.Добавить("СтрGUID", ВнешняяБаза.NewObject("ОписаниеТипов", "Строка"));
// Проходим по таблице в COM-базе и заполняем строковые идентификаторы
// Это все еще медленно, но мы делаем это ОДИН раз
Для Каждого Стр Из ТЗВнешняя Цикл
Стр.СтрGUID = ВнешняяБаза.XMLСтрока(Стр.СсылкаОрг.УникальныйИдентификатор());
КонецЦикла;
// Теперь переносим данные в нашу локальную ТЗ
МояТЗ = Новый ТаблицаЗначений;
МояТЗ.Колонки.Добавить("Организация", Новый ОписаниеТипов("СправочникСсылка.Организации"));
МояТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Для Каждого СтрВнеш Из ТЗВнешняя Цикл
НоваяСтр = МояТЗ.Добавить();
НоваяСтр.Организация = Справочники.Организации.ПолучитьСсылку(Новый УникальныйИдентификатор(СтрВнеш.СтрGUID));
НоваяСтр.Сумма = СтрВнеш.Сумма;
КонецЦикла;
Нужно учитывать важную особенность метода ПолучитьСсылку(). Этот метод работает "вслепую": он создает ссылку в памяти на основе переданного GUID, но не проверяет, существует ли такой объект в базе данных на самом деле. Если в базе-приемнике объект еще не был создан или был удален, вы получите так называемую "битую ссылку" (Объект не найден).
Чтобы выяснить, существует ли объект, мы рекомендуем после получения ссылки выполнить проверку:
Если СсылкаЛокальная.ПолучитьОбъект() = Неопределено Тогда
// Объекта с таким GUID в нашей базе нет
// Здесь можно либо пропустить запись, либо вывести предупреждение
КонецЕсли;
В условиях РИБ объекты обычно идентичны, но рассинхронизация может случиться из-за настроек фильтрации обмена или ошибок передачи данных, поэтому такая проверка никогда не будет лишней.
Ситуация усложняется, если поле в регистре имеет составной тип (например, реквизит Владелец может быть и СправочникСсылка.Контрагенты, и СправочникСсылка.ФизическиеЛица). В этом случае нам нужно сначала понять, к какому метаданному относится COM-объект.
Разберем, как определить тип объекта через COM:
ВнешняяБаза.XMLТипЗнч(Выборка.Поле). Он вернет структуру с именем типа и пространством имен.Справочники.Контрагенты или Справочники.ФизическиеЛица).Пример получения имени метаданных:
ИмяМетаданных = Выборка.Поле.Метаданные().Имя;
Процесс получения реальных ссылок из COM-соединения сводится к трем этапам: извлечение GUID во внешней базе -> передача его строкового представления -> генерация ссылки в текущей базе по этому идентификатору.
Основные рекомендации:
XMLСтрока() для сериализации идентификаторов, чтобы избежать проблем с форматом строки.ТаблицуЗначений на стороне источника.ПолучитьСсылку() не гарантирует существования объекта в базе.Следуя этим правилам, вы сможете эффективно обмениваться данными между базами, сохраняя ссылочную целостность и обеспечивая высокую скорость работы ваших обработок.