При реализации прямого обмена данными между двумя информационными базами 1С через COM-соединение разработчики часто сталкиваются с проблемой передачи сложных типов данных (массивов, списков значений) в параметры запроса. Ошибка возникает из-за того, что объекты, созданные в текущей («родной») базе, не распознаются внешней базой как корректные типы данных. В этой статье мы подробно разберем, как правильно организовать передачу данных, почему возникает ошибка «Несоответствие типов» и как оптимизировать процесс обмена.
Разберем ситуацию: вы создаете объект СписокЗначений в коде текущей базы, наполняете его ссылками или строками, а затем пытаетесь передать этот список в запрос, который выполняется через COMСоединение. В этот момент внешняя база получает не коллекцию значений, а непонятный ей COMОбъект. Это происходит потому, что область памяти текущего сеанса и область памяти COM-сервера изолированы друг от друга.
Проанализируем типичную ошибку. Если написать так:
СписокСсылок = Новый СписокЗначений;
СписокСсылок.Добавить(НужнаяСсылка);
Запрос = COMБаза.NewObject("Запрос");
Запрос.Текст = "ВЫБРАТЬ * ИЗ Документ.Приказ ГДЕ Ссылка В (&Список)";
Запрос.УстановитьПараметр("Список", СписокСсылок); // ОШИБКА!
Внешняя база не поймет, что такое СписокСсылок, так как этот объект был рожден в другой программной среде. Для решения этой задачи нам нужно создать список «внутри» той базы, к которой мы подключились.
Чтобы внешняя база «признала» список значений своим, его необходимо инициализировать методом NewObject самого COM-соединения. Рассмотрим правильный алгоритм действий по шагам:
СписокЗначений или Массив непосредственно во внешней базе.Посмотрим на пример кода, который решает эту задачу:
// 1. Подключаемся к базе
Коннектор = Новый COMОбъект("V83.COMConnector");
БД2 = Коннектор.Connect("Srvr='ServerName';Ref='BaseName';Usr='Login';Pwd='Password';");
// 2. Создаем список значений ВНУТРИ внешней базы
СписокДляЗапроса = БД2.NewObject("СписокЗначений");
// 3. Заполняем его номерами или другими идентификаторами
Для Каждого СтрокаДокумента Из СписокВыбранных Цикл
// Добавляем только примитивные типы: Строка, Число, Дата
СписокДляЗапроса.Добавить(СтрокаДокумента.Номер);
КонецЦикла;
// 4. Выполняем запрос во внешней базе
Запрос = БД2.NewObject("Запрос");
Запрос.Текст =
"ВЫБРАТЬ
| Приказ.Ссылка
|ИЗ
| Документ.ПриказОПростоях КАК Приказ
|ГДЕ
| Приказ.Номер В (&Список)";
Запрос.УстановитьПараметр("Список", СписокДляЗапроса);
Результат = Запрос.Выполнить().Выгрузить();
Использование поиска по Номер и Дата — не самый надежный способ, так как номера могут дублироваться или иметь разные префиксы. Самый точный способ сопоставления объектов между базами — использование уникальных идентификаторов (GUID). Выясним, как передать ссылку через её строковое представление.
Рассмотрим алгоритм передачи через UUID:
// В исходной базе получаем УникальныйИдентификатор ссылки
СтрокаGUID = Строка(ДокументСсылка.УникальныйИдентификатор());
// Во внешней базе создаем объект UUID из строки
ВнешнийGUID = БД2.NewObject("UUID", СтрокаGUID);
// Получаем ссылку во внешней базе
СсылкаВБД2 = БД2.Документы.ПриказОПростоях.ПолучитьСсылку(ВнешнийGUID);
// Теперь эту ссылку можно добавить в список, созданный через БД2.NewObject("СписокЗначений")
СписокGUID = БД2.NewObject("СписокЗначений");
СписокGUID.Добавить(СсылкаВБД2);
Этот метод гарантирует, что вы найдете именно тот документ, который нужен, даже если его номер был изменен.
Если вам нужно перенести тысячи документов, использование конструкции В (&Список) может привести к замедлению работы SQL-сервера или ошибке переполнения стека. В таких ситуациях проанализируем использование временных таблиц.
Вместо передачи огромного списка, мы можем создать ТаблицуЗначений через COM, загрузить в неё данные и передать её в запрос как таблицу для помещения во временную таблицу. Однако стоит помнить, что работа с ТаблицейЗначений через COM-интерфейс «построчно» (в цикле) очень медленная. Каждое обращение к свойству или методу через точку — это сетевой вызов.
Рекомендация: старайтесь минимизировать количество вызовов через точку. Если нужно прочитать много данных, лучше выполнить один запрос во внешней базе, выгрузить его в ТаблицуЗначений (метод Выгрузить()), и уже эту таблицу анализировать в локальном коде.
1. Разрядность систем (32/64 бит)
Если ваш сервер 1С работает в 64-битном режиме, убедитесь, что на сервере зарегистрирован 64-битный COM-коннектор (comcntr.dll). В противном случае вы получите ошибку «Класс не зарегистрирован».
2. Передача типов
При прямом подключении корректно передаются только примитивные типы: Число, Строка, Дата, Булево. Объекты типа Перечисление передать напрямую нельзя. Выход: передавать имя перечисления строкой и в запросе использовать конструкцию ЗНАЧЕНИЕ(Перечисление.Имя.Значение) или искать его во внешней базе через Перечисления[ИмяПеречисления][ИмяЗначения].
3. Завершение соединения
После завершения обмена всегда обнуляйте переменные, содержащие COM-объекты. Это освобождает лицензию и закрывает сеанс на сервере:
БД2 = Неопределено;
Коннектор = Неопределено;
Мы выяснили, что основной секрет успешного обмена через прямое подключение заключается в создании объектов коллекций (Списков, Массивов) в контексте той базы, где будет выполняться запрос. Использование метода COM.NewObject("СписокЗначений") позволяет избежать ошибок типизации и корректно передавать параметры в запросы. Применение GUID в качестве идентификатора делает обмен надежным, а минимизация вызовов «через точку» — быстрым.