Почему при обмене через COM в 1С прилетают нулевые значения и как это исправить

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

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

Суть проблемы: исчезающие данные

Типичная ситуация выглядит следующим образом: мы создаем запрос в базе-источнике через COM-соединение, перебираем выборку и заполняем табличную часть документа в текущей базе. Проанализируем стандартный код, который может давать сбои:


ЗапросТовары = Соединение.NewObject("Query");
ЗапросТовары.SetParameter("Ссылка", ТекущийДокумент);
ЗапросТовары.Text = 
"ВЫБРАТЬ
|    РеализацияТоваровТовары.Количество КАК Количество
|ИЗ  
|    Документ.РеализацияТоваров.Товары КАК РеализацияТоваровТовары
|ГДЕ
|    РеализацияТоваровТовары.Ссылка = &Ссылка";

РезультатТовары = ЗапросТовары.Выполнить();
ВыборкаТовары = РезультатТовары.Выбрать();

Пока ВыборкаТовары.Следующий() Цикл
    НоваяСтрока = ОбДокумент.Товары.Добавить();    
    НоваяСтрока.Количество = ВыборкаТовары.Количество;
КонецЦикла;

В этой схеме периодически и «рандомно» в реквизит НоваяСтрока.Количество записывается 0. При этом, если поставить точку останова сразу после присваивания, выясняется удивительная вещь: в ВыборкаТовары.Количество значение есть (например, 3), но в строке документа уже стоит 0. Это наталкивает на мысль, что проблема кроется в механизме передачи данных между процессами.

Разберем причину: нестабильность COM-прокси

Выясним причину такого поведения. Когда мы обращаемся к ВыборкаТовары.Количество, мы не получаем число напрямую. Мы обращаемся к COM-объекту (прокси). В момент обращения операционная система выполняет маршалинг — передачу запроса из одного процесса (базы-приемника) в другой (базу-источник).

Если в этот момент происходит микросекундная задержка сети, блокировка потока или нехватка ресурсов на сервере, COM-интерфейс может не успеть вернуть актуальное значение и отдает «значение по умолчанию» для данного типа. Для числовых полей это 0. При этом ошибка исключения (Exception) не генерируется, и программа продолжает работу с неверными данными.

Решение 1: Метод «двойного присваивания» (программный костыль)

Как показала практика обсуждения, самым быстрым способом заставить систему работать корректно является повторное обращение к свойству, если первое вернуло ноль. Посмотрим на пример модифицированного кода:


Пока ВыборкаТовары.Следующий() Цикл
    НоваяСтрока = ОбДокумент.Товары.Добавить();    
    НоваяСтрока.Количество = ВыборкаТовары.Количество;
    
    // Если прилетел "фантомный" ноль, пробуем еще раз
    Если НоваяСтрока.Количество = 0 И ВыборкаТовары.Количество <> 0 Тогда 
        НоваяСтрока.Количество = ВыборкаТовары.Количество;
    КонецЕсли;
КонецЦикла;

Проанализируем ситуацию: почему это помогает? Второе обращение к ВыборкаТовары.Количество происходит через долю секунды, когда канал связи уже «прогрет» или ресурс освободился. Это решение не является элегантным, но оно эффективно устраняет проблему «фантомных нулей» без глобальной переделки обмена.

Решение 2: Явное приведение типов и промежуточные переменные

Иногда 1С некорректно обрабатывает тип Variant, приходящий из COM. Чтобы минимизировать ошибки, попробуем использовать промежуточную переменную и явную функцию Число(). Это заставит платформу выполнить преобразование типа на стороне приемника более жестко.


Пока ВыборкаТовары.Следующий() Цикл
    НоваяСтрока = ОбДокумент.Товары.Добавить();    
    ВремКоличество = ВыборкаТовары.Количество; // Получаем значение во временную переменную
    НоваяСтрока.Количество = Число(ВремКоличество); 
КонецЦикла;

Важно также проверить, не срабатывают ли в момент добавления строки обработчики событий в модуле документа (например, ПриИзменении), которые могут обнулять количество из-за незаполненных единиц измерения или коэффициентов. Для этого перед заполнением ТЧ всегда устанавливайте флаг: ОбДокумент.ОбменДанными.Загрузка = Истина;.

Решение 3: Уход от итерации по COM-объектам (Сериализация)

Самый надежный способ избежать проблем с COM — это минимизировать количество обращений между базами. Вместо того чтобы тянуть каждое поле каждой строки через Выборка.Следующий(), лучше выгрузить все данные одним пакетом.

Рассмотрим алгоритм правильного обмена:

  1. В базе-источнике формируем Таблицу Значений с результатами запроса.
  2. Сериализуем эту таблицу в строку (через XDTO, JSON или ЗначениеВСтрокуВнутр).
  3. Передаем одну большую строку в базу-приемник.
  4. В базе-приемнике десериализуем строку обратно в таблицу и спокойно обходим её циклом без использования COM-соединения внутри итераций.

Посмотрим на пример с использованием JSON (актуально для современных платформ):


// В базе источнике (выполняется через Соединение)
ТЗ = Запрос.Выполнить().Выгрузить();
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, ТЗ);
СтрокаДанных = ЗаписьJSON.Закрыть();

// В базе приемнике
Данные = ПрочитатьJSON(СтрокаДанных); // Теперь у нас массив структур, а не COM-объекты
Для Каждого СтрокаИзДанных Из Данные Цикл
    НоваяСтрока = ДокументОбъект.Товары.Добавить();
    НоваяСтрока.Количество = СтрокаИзДанных.Количество; // Здесь ошибки исключены
КонецЦикла;

Рекомендации по настройке сервера

Если вы не можете изменить код, проанализируем настройки окружения. Проблема часто обостряется при высокой нагрузке на rphost. Попробуйте следующее:

Резюме: Если вы столкнулись с «нулями» в COM-обмене, начните с внедрения проверки Если НоваяСтрока.Количество = 0. Если это помогает — значит, вы столкнулись с нестабильностью связи. В долгосрочной перспективе планируйте переход на обмен через JSON или Web-сервисы, так как технология COM официально считается устаревшей и менее стабильной в высоконагруженных системах.

← На главную