При интеграции двух информационных баз 1С с помощью внешнего соединения часто возникает задача синхронизации справочников с сохранением исходных идентификаторов. При попытке передать ссылку на объект или уникальный идентификатор напрямую через границу COM-соединения разработчики сталкиваются с исключением: Ошибка при вызове конструктора (УникальныйИдентификатор): Недопустимое значение параметра (параметр номер '1'). Разберем детально, почему возникает данная ошибка и как правильно реализовать передачу идентификаторов и создание элементов с заданным GUID.
Рассмотрим типовую ситуацию. Из базы-источника вызывается экспортная функция общего модуля базы-приемника, куда в качестве параметра передается ссылка на объект:
// Вызов в базе-источнике
Ответ = БазаШК.СоздатьНоменклатуру(СсылкаНоменклатура);
В модуле базы-приемника код пытается извлечь УникальныйИдентификатор и получить по нему ссылку для нового элемента:
Функция СоздатьНоменклатуру(Номенклатура) Экспорт
ГУИД = Номенклатура.УникальныйИдентификатор;
РезСсылка = Справочники._Номенклатура.ПолучитьСсылку(Новый УникальныйИдентификатор(Строка(ГУИД)));
// ... дальнейшие действия
КонецФункции
На строке инициализации конструктора Новый УникальныйИдентификатор(...) выполнение прерывается с ошибкой. Выясним причину такого поведения платформы 1С:
СправочникСсылка или УникальныйИдентификатор) через COM-соединение база-приемник получает не нативный объект 1С, а специальную интерфейсную обертку типа COMОбъект.Строка(), примененная к COMОбъекту в удаленной базе, не преобразует значение в нативную строку формата GUID, а возвращает ссылку на COM-объект или его внутреннее системное описание.Новый УникальныйИдентификатор(Параметр) ожидает на вход исключительно строку стандартного формата (36 символов с дефисами). Передача объекта типа COMОбъект приводит к ошибке типизации.Номенклатура.Код, Номенклатура.Наименование) порождает множество межпроцессных сетевых вызовов, что существенно замедляет пакетную обработку данных.Самый надежный и быстродействующий способ — выполнять преобразование типов на стороне базы-источника до момента отправки данных через COM-соединение. Передавать следует исключительно примитивные типы (строки, числа, даты, булево) или структуры с примитивными полями.
Разберем по шагам правильную организацию обмена.
Шаг 1. Формирование данных в базе-источнике:
Для Каждого СтрокаТЧ Из ТаблицаНоменклатуры Цикл
ПараметрыЭлемента = Новый Структура;
ПараметрыЭлемента.Вставить("ГУИД", Строка(СтрокаТЧ.Номенклатура.УникальныйИдентификатор()));
ПараметрыЭлемента.Вставить("Код", СтрокаТЧ.Номенклатура.Код);
ПараметрыЭлемента.Вставить("Наименование", СтрокаТЧ.Номенклатура.Наименование);
// Передаем структуру с примитивными данными
Результат = БазаПриемник.СоздатьНоменклатуру(ПараметрыЭлемента);
КонецЦикла;
Шаг 2. Прием данных и создание элемента с фиксированным GUID в базе-приемнике:
Функция СоздатьНоменклатуру(ПараметрыЭлемента) Экспорт
СтрокаГУИД = СокрЛП(ПараметрыЭлемента.ГУИД);
Идентификатор = Новый УникальныйИдентификатор(СтрокаГУИД);
// Получаем ссылку по переданному GUID
ЦелеваяСсылка = Справочники.Номенклатура.ПолучитьСсылку(Идентификатор);
// Проверяем существование объекта в базе
ОбъектНоменклатура = ЦелеваяСсылка.ПолучитьОбъект();
Если ОбъектНоменклатура = Неопределено Тогда
// Создаем новый объект и фиксируем за ним ссылку ДО первой записи
ОбъектНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
ОбъектНоменклатура.УстановитьСсылкуНового(ЦелеваяСсылка);
КонецЕсли;
ОбъектНоменклатура.Код = ПараметрыЭлемента.Код;
ОбъектНоменклатура.Наименование = ПараметрыЭлемента.Наименование;
Попытка
ОбъектНоменклатура.Записать();
Возврат Истина;
Исключение
ЗаписьЖурналаРегистрации("Синхронизация COM", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
КонецФункции
Если требуется гарантировать полную корректность формата идентификатора при передаче между базами 1С, рекомендуется использовать встроенные функции платформы для работы с XML-строками: XMLСтрока() и XMLЗначение().
Посмотрим на пример реализации:
В базе-источнике формируем строковое представление ссылки:
// XMLСтрока сразу возвращает чистый строковый GUID
СтрокаGUID = XMLСтрока(СсылкаНоменклатура);
Ответ = БазаПриемник.СоздатьНоменклатуруПоXML(СтрокаGUID, СсылкаНоменклатура.Наименование, СсылкаНоменклатура.Код);
В базе-приемнике восстанавливаем ссылку напрямую через XMLЗначение:
Функция СоздатьНоменклатуруПоXML(СтрокаGUID, Наименование, Код) Экспорт
// Получаем типизированную ссылку напрямую из XML-строки
ЦелеваяСсылка = XMLЗначение(Тип("СправочникСсылка.Номенклатура"), СтрокаGUID);
ОбъектНоменклатура = ЦелеваяСсылка.ПолучитьОбъект();
Если ОбъектНоменклатура = Неопределено Тогда
ОбъектНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
ОбъектНоменклатура.УстановитьСсылкуНового(ЦелеваяСсылка);
КонецЕсли;
ОбъектНоменклатура.Наименование = Наименование;
ОбъектНоменклатура.Код = Код;
ОбъектНоменклатура.Записать();
Возврат ЦелеваяСсылка;
КонецФункции
Проанализируем технические нюансы работы метода УстановитьСсылкуНового(), нарушение которых приводит к труднодиагностируемым ошибкам:
УстановитьСсылкуНового() можно вызывать только для нового, еще не записанного в базу объекта (созданного через СоздатьЭлемент()) и строго до первого вызова метода Записать().GUID, ошибка контроля уникальности первичного ключа возникнет только при выполнении метода Записать().ЦелеваяСсылка.ПолучитьОбъект() выполняет полное считывание всех табличных частей. Для высоконагруженных обменов проверку наличия ссылки лучше выполнять легковесным запросом к таблице справочника:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Спр.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Спр
|ГДЕ
| Спр.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", ЦелеваяСсылка);
Результат = Запрос.Выполнить();
Существует = НЕ Результат.Пустой();