Почему при обращении через COM возникает ошибка «Значение не является значением объектного типа (ОбменДанными)»?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия IT и автоматизация бизнеса
← На главную

При организации интеграции между конфигурациями 1С с помощью внешнего соединения через COM-объект разработчики нередко сталкиваются с ошибкой времени выполнения:

{Форма.Форма.Форма(446)}: Значение не является значением объектного типа (ОбменДанными)

Ошибка проявляется в момент попытки включить режим загрузки данных у полученного объекта:

Договор_БУХ_Объект.ОбменДанными.Загрузка = Истина;

Давайте подробно разберем, почему возникает данное исключение, какие особенности платформы 1С приводят к неожиданному поведению кода и как правильно реализовать обновление или создание объектов через COM-соединение.

Причина ошибки: особенность работы метода ПолучитьСсылку

Рассмотрим фрагмент кода, в котором возникает ошибка:


УИД_БУХ = Бухгалтерия.NewObject("УникальныйИдентификатор", Строка(Договор.УникальныйИдентификатор()));
Договор_БУХ = Бухгалтерия.Справочники.ДоговорыКонтрагентов.ПолучитьСсылку(УИД_БУХ);

Если НЕ Договор_БУХ.Пустая() Тогда
    // Попытка получить объект
    Договор_БУХ_Объект = Договор_БУХ.ПолучитьОбъект();
    Договор_БУХ_Объект.ОбменДанными.Загрузка = Истина; // Ошибка здесь!
    // ...
КонецЕсли;

На первый взгляд кажется, что условие Если НЕ Договор_БУХ.Пустая() Тогда защищает от обращения к несуществующим данным. Однако здесь кроется фундаментальная особенность платформы 1С:

  1. Метод ПолучитьСсылку() не обращается к базе данных. Он конструирует ссылку на основании переданного УникальныйИдентификатор исключительно в оперативной памяти. Платформа формирует ссылку с заданным GUID вне зависимости от того, записан ли такой элемент в таблице информационной базы.
  2. Метод ссылки Пустая() всегда возвращает Ложь. Поскольку переданный GUID содержит конкретное значение (не является нулевым идентификатором 00000000-0000-0000-0000-000000000000), ссылка не считается пустой. Поэтому проверка НЕ Договор_БУХ.Пустая() всегда будет истинной, даже если объекта в базе не существует.
  3. Чтение реквизитов несуществующей ссылки не вызывает ошибок. Если обратиться к Договор_БУХ.Реквизит1, платформа вернет значение по умолчанию для соответствующего типа (пустую строку, 0 или пустую ссылку), создавая иллюзию корректной работы.
  4. Метод ПолучитьОбъект() возвращает Неопределено. Когда платформа пытается вычитать объект из базы по несуществующей ссылке, физическая запись не находится, и метод возвращает значение типа Неопределено. В результате следующая строка кода пытается обратиться к свойству ОбменДанными у значения Неопределено, что и вызывает ошибку «Значение не является значением объектного типа».

Кроме того, ПолучитьОбъект() может возвращать Неопределено даже при наличии физической записи в базе, если у пользователя внешнего COM-соединения действуют ограничения прав на уровне записей (RLS) или отсутствуют права на чтение/изменение данного справочника.

Решение 1. Явная проверка результата ПолучитьОбъект на Неопределено

Разберем самый прямой способ исправления ошибки: проверка переменной объекта перед обращением к ее свойствам и методам. Проанализируем корректный код:


УИД_БУХ = Бухгалтерия.NewObject("УникальныйИдентификатор", Строка(Договор.УникальныйИдентификатор()));
Договор_БУХ = Бухгалтерия.Справочники.ДоговорыКонтрагентов.ПолучитьСсылку(УИД_БУХ);

Договор_БУХ_Объект = Договор_БУХ.ПолучитьОбъект();

Если Договор_БУХ_Объект <> Неопределено Тогда
    Договор_БУХ_Объект.ОбменДанными.Загрузка = Истина;
    Договор_БУХ_Объект.Реквизит1 = Договор.Реквизит1;
    Договор_БУХ_Объект.Записать();
Иначе
    // Элемент с таким УИД в базе-приемнике не найден
    // Здесь можно выполнить создание нового элемента
КонецЕсли;

Такой подход полностью устраняет падение программы, однако требует внимательности, если логика обмена предполагает создание нового элемента при отсутствии старого.

Решение 2. Создание нового объекта с сохранением GUID

Если цель обмена состоит в том, чтобы обновить объект при наличии либо создать его с тем же уникальным идентификатором при отсутствии, разберем паттерн с методом УстановитьСсылкуНового():


УИД_БУХ = Бухгалтерия.NewObject("УникальныйИдентификатор", Строка(Договор.УникальныйИдентификатор()));
Договор_БУХ = Бухгалтерия.Справочники.ДоговорыКонтрагентов.ПолучитьСсылку(УИД_БУХ);

ОбъектБУХ = Договор_БУХ.ПолучитьОбъект();

Если ОбъектБУХ = Неопределено Тогда
    // Создаем новый элемент, если объекта в базе еще нет
    ОбъектБУХ = Бухгалтерия.Справочники.ДоговорыКонтрагентов.СоздатьЭлемент();
    ОбъектБУХ.УстановитьСсылкуНового(Договор_БУХ);
КонецЕсли;

// Теперь ОбъектБУХ гарантированно содержит объектную сущность
ОбъектБУХ.ОбменДанными.Загрузка = Истина;
ОбъектБУХ.Наименование = Договор.Наименование;
ОбъектБУХ.Реквизит1 = Договор.Реквизит1;
ОбъектБУХ.Записать();

Метод УстановитьСсылкуНового() назначает новому объекту заранее подготовленную ссылку, обеспечивая синхронизацию идентификаторов между базами данных.

Решение 3. Оптимизация проверки существования через запрос

Вызов ПолучитьОбъект() считывает из базы всю структуру данных (включая все табличные части) и может захватывать управляемые блокировки. Если требуется только проверить, существует ли объект, быстрее и экономнее выполнить запрос во внешней базе:


ЗапросБУХ = Бухгалтерия.NewObject("Запрос");
ЗапросБУХ.Текст = 
    "ВЫБРАТЬ ПЕРВЫЕ 1
    |	Договоры.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.ДоговорыКонтрагентов КАК Договоры
    |ГДЕ
    |	Договоры.Ссылка = &Ссылка";

ЗапросБУХ.УстановитьПараметр("Ссылка", Договор_БУХ);
Выборка = ЗапросБУХ.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда
    Договор_БУХ_Объект = Договор_БУХ.ПолучитьОбъект();
    Договор_БУХ_Объект.ОбменДанными.Загрузка = Истина;
    Договор_БУХ_Объект.Реквизит1 = Договор.Реквизит1;
    Договор_БУХ_Объект.Записать();
Иначе
    // Действия при отсутствии записи в базе
КонецЕсли;

Важные рекомендации при работе с ОбменДанными через COM

  1. Назначение свойства ОбменДанными.Загрузка = Истина: оно отключает стандартные проверки в обработчиках ПередЗаписью и ПриЗаписи (при условии, что в модулях конфигурации прописано стандартное условие Если ОбменДанными.Загрузка Тогда Возврат; КонецЕсли;). Однако это свойство не отменяет ограничения базы данных на уровне СУБД (например, уникальность предопределенных полей и контроль типов данных).
  2. Безопасность транзакций: при вызове методов записи через COM всегда используйте явные транзакции и блоки перехвата ошибок Попытка ... Исключение, чтобы обрыв сетевого соединения или сбой записи не оставили базу в частично несогласованном состоянии:
    
    Бухгалтерия.НачатьТранзакцию();
    Попытка
        Договор_БУХ_Объект.Записать();
        Бухгалтерия.ЗафиксироватьТранзакцию();
    Исключение
        Бухгалтерия.ОтменитьТранзакцию();
        ВызватьИсключение "Ошибка при записи через COM: " + ОписаниеОшибки();
    КонецПопытки;
    
  3. Права пользователя: убедитесь, что учетная запись, под которой создается COM-соединение, обладает полными правами или явными правами на редактирование целевых объектов без ограничений RLS.
← На главную