Работа с технологией COM-соединения (COMConnector) часто сопровождается необъяснимыми на первый взгляд ошибками. Одна из самых коварных ситуаций — когда при переносе данных из базы-источника в базу-приемник числовые значения (например, количество или сумма) случайным образом заменяются на ноль, хотя в исходной базе данные заполнены корректно. Рассмотрим подробнее причины этого явления и разберем способы решения проблемы.
Типичная ситуация выглядит следующим образом: мы создаем запрос в базе-источнике через COM-соединение, перебираем выборку и заполняем табличную часть документа в текущей базе. Проанализируем стандартный код, который может давать сбои:
ЗапросТовары = Соединение.NewObject("Query");
ЗапросТовары.SetParameter("Ссылка", ТекущийДокумент);
ЗапросТовары.Text =
"ВЫБРАТЬ
| РеализацияТоваровТовары.Количество КАК Количество
|ИЗ
| Документ.РеализацияТоваров.Товары КАК РеализацияТоваровТовары
|ГДЕ
| РеализацияТоваровТовары.Ссылка = &Ссылка";
РезультатТовары = ЗапросТовары.Выполнить();
ВыборкаТовары = РезультатТовары.Выбрать();
Пока ВыборкаТовары.Следующий() Цикл
НоваяСтрока = ОбДокумент.Товары.Добавить();
НоваяСтрока.Количество = ВыборкаТовары.Количество;
КонецЦикла;
В этой схеме периодически и «рандомно» в реквизит НоваяСтрока.Количество записывается 0. При этом, если поставить точку останова сразу после присваивания, выясняется удивительная вещь: в ВыборкаТовары.Количество значение есть (например, 3), но в строке документа уже стоит 0. Это наталкивает на мысль, что проблема кроется в механизме передачи данных между процессами.
Выясним причину такого поведения. Когда мы обращаемся к ВыборкаТовары.Количество, мы не получаем число напрямую. Мы обращаемся к COM-объекту (прокси). В момент обращения операционная система выполняет маршалинг — передачу запроса из одного процесса (базы-приемника) в другой (базу-источник).
Если в этот момент происходит микросекундная задержка сети, блокировка потока или нехватка ресурсов на сервере, COM-интерфейс может не успеть вернуть актуальное значение и отдает «значение по умолчанию» для данного типа. Для числовых полей это 0. При этом ошибка исключения (Exception) не генерируется, и программа продолжает работу с неверными данными.
Как показала практика обсуждения, самым быстрым способом заставить систему работать корректно является повторное обращение к свойству, если первое вернуло ноль. Посмотрим на пример модифицированного кода:
Пока ВыборкаТовары.Следующий() Цикл
НоваяСтрока = ОбДокумент.Товары.Добавить();
НоваяСтрока.Количество = ВыборкаТовары.Количество;
// Если прилетел "фантомный" ноль, пробуем еще раз
Если НоваяСтрока.Количество = 0 И ВыборкаТовары.Количество <> 0 Тогда
НоваяСтрока.Количество = ВыборкаТовары.Количество;
КонецЕсли;
КонецЦикла;
Проанализируем ситуацию: почему это помогает? Второе обращение к ВыборкаТовары.Количество происходит через долю секунды, когда канал связи уже «прогрет» или ресурс освободился. Это решение не является элегантным, но оно эффективно устраняет проблему «фантомных нулей» без глобальной переделки обмена.
Иногда 1С некорректно обрабатывает тип Variant, приходящий из COM. Чтобы минимизировать ошибки, попробуем использовать промежуточную переменную и явную функцию Число(). Это заставит платформу выполнить преобразование типа на стороне приемника более жестко.
Пока ВыборкаТовары.Следующий() Цикл
НоваяСтрока = ОбДокумент.Товары.Добавить();
ВремКоличество = ВыборкаТовары.Количество; // Получаем значение во временную переменную
НоваяСтрока.Количество = Число(ВремКоличество);
КонецЦикла;
Важно также проверить, не срабатывают ли в момент добавления строки обработчики событий в модуле документа (например, ПриИзменении), которые могут обнулять количество из-за незаполненных единиц измерения или коэффициентов. Для этого перед заполнением ТЧ всегда устанавливайте флаг: ОбДокумент.ОбменДанными.Загрузка = Истина;.
Самый надежный способ избежать проблем с COM — это минимизировать количество обращений между базами. Вместо того чтобы тянуть каждое поле каждой строки через Выборка.Следующий(), лучше выгрузить все данные одним пакетом.
Рассмотрим алгоритм правильного обмена:
Таблицу Значений с результатами запроса.XDTO, JSON или ЗначениеВСтрокуВнутр).Посмотрим на пример с использованием JSON (актуально для современных платформ):
// В базе источнике (выполняется через Соединение)
ТЗ = Запрос.Выполнить().Выгрузить();
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ТЗ);
СтрокаДанных = ЗаписьJSON.Закрыть();
// В базе приемнике
Данные = ПрочитатьJSON(СтрокаДанных); // Теперь у нас массив структур, а не COM-объекты
Для Каждого СтрокаИзДанных Из Данные Цикл
НоваяСтрока = ДокументОбъект.Товары.Добавить();
НоваяСтрока.Количество = СтрокаИзДанных.Количество; // Здесь ошибки исключены
КонецЦикла;
Если вы не можете изменить код, проанализируем настройки окружения. Проблема часто обостряется при высокой нагрузке на rphost. Попробуйте следующее:
1C:Enterprise 8.3 Remote Connector установлены адекватные тайм-ауты.comcntr.dll) совпадают до последнего знака.Резюме: Если вы столкнулись с «нулями» в COM-обмене, начните с внедрения проверки Если НоваяСтрока.Количество = 0. Если это помогает — значит, вы столкнулись с нестабильностью связи. В долгосрочной перспективе планируйте переход на обмен через JSON или Web-сервисы, так как технология COM официально считается устаревшей и менее стабильной в высоконагруженных системах.