При интеграции внешних приложений с системой 1С:Предприятие 8.3 через механизм COM-соединения (COMConnector) разработчики часто сталкиваются с необъяснимым поведением: запрос, который идеально работает в консоли запросов внутри 1С, возвращает пустые значения (NULL или Nothing) для определенных полей при вызове из внешнего кода на VB.NET, C# или Delphi. Одной из самых коварных проблем является работа с полем Владелец.
В этой статье мы подробно разберем, почему возникает эта ошибка, проанализируем механизмы работы COM-интерфейса 1С и рассмотрим несколько способов решения проблемы, от простых правок запроса до архитектурных оптимизаций.
Рассмотрим типичный сценарий. Программисту необходимо выгрузить список банковских счетов из 1С во внешнюю систему. Он составляет стандартный запрос к справочнику БанковскиеСчета:
ВЫБРАТЬ
БанковскиеСчета.Ссылка КАК Ссылка,
БанковскиеСчета.Наименование КАК Наименование,
БанковскиеСчета.НомерСчета КАК НомерСчета,
БанковскиеСчета.Владелец КАК Владелец
ИЗ
Справочник.БанковскиеСчета КАК БанковскиеСчета
В консоли запросов 1С поле Владелец корректно заполняется ссылками на Организации или Контрагентов. Однако при обращении через COM-объект в коде на Visual Basic .NET:
query = Helper1C.CreateComObject(databaseName, "Запрос")
query.Text = "ВЫБРАТЬ ... БанковскиеСчета.Владелец КАК Владелец ..."
cursor = query.Выполнить().Выбрать()
While Convert.ToBoolean(cursor.Следующий())
' Здесь значение cursor.Владелец всегда Nothing
End While
Проблема заключается не в данных и не в правах доступа, а в конфликте имен в объектной модели 1С при использовании позднего связывания.
Проанализируем объект ВыборкаИзРезультатаЗапроса, который возвращается методом Выбрать(). Этот объект является динамическим: 1С автоматически добавляет в него свойства, имена которых совпадают с псевдонимами (синонимами) полей из вашего запроса. Если вы написали ВЫБРАТЬ НомерСчета КАК Номер, у объекта появится свойство Номер.
Однако у объекта выборки есть и свои встроенные методы. В частности, метод Владелец(), который используется для работы с иерархическими выборками (чтобы получить родительскую запись при обходе). Когда внешний COM-клиент (например, среда .NET) обращается к cursor.Владелец, он видит два варианта: встроенный метод объекта 1С и динамическое поле данных.
В большинстве случаев COM-интерфейс отдает приоритет методу. Поскольку метод Владелец() ожидает определенных условий вызова, при простом обращении как к свойству он возвращает пустое значение или ошибку, «перекрывая» собой реальные данные из колонки запроса.
Самый простой и эффективный способ решить проблему — переименовать колонку в тексте запроса так, чтобы она не совпадала ни с одним системным методом выборки 1С. Рассмотрим исправленный текст запроса:
ВЫБРАТЬ
БанковскиеСчета.Ссылка КАК Ссылка,
БанковскиеСчета.Наименование КАК Наименование,
БанковскиеСчета.Владелец КАК ВладелецСчета // Заменяем "Владелец" на уникальное имя
ИЗ
Справочник.БанковскиеСчета КАК БанковскиеСчета
После такого изменения в коде VB.NET или C# вы будете обращаться к cursor.ВладелецСчета. Поскольку у объекта 1С нет встроенного метода с таким именем, COM-клиент однозначно интерпретирует это как обращение к полю данных, и вы получите корректную ссылку на объект.
Конфликт с Владелец — не единственный. Проанализируем список имен, которые крайне не рекомендуется использовать в качестве псевдонимов колонок при работе через COM:
Важный совет: Старайтесь всегда добавлять к стандартным именам полей префиксы или суффиксы, например: ТекущийВладелец, КоличествоТовара, ЭтоСледующийЭлемент.
Если вам необходимо избежать конфликтов имен в динамически формируемых запросах, рассмотрим вариант с выгрузкой результата в таблицу значений. Объект ТаблицаЗначений через COM ведет себя более предсказуемо, так как доступ к его колонкам осуществляется через коллекцию Колонки или обращением к строке.
Разберем пример на стороне 1С (внутри COM-вызова):
Результат = Запрос.Выполнить().Выгрузить();
// Теперь передаем эту таблицу в VB.NET
// Обращение в VB.NET будет выглядеть так:
For Each row In Результат
owner = row.Владелец ' В таблице значений это свойство строки, а не метод выборки
Next
Этот способ надежнее, но стоит помнить о производительности: передача больших таблиц значений через COM может быть медленнее, чем использование итератора выборки.
Выясним еще одну причину, по которой Владелец может возвращать NULL. В 1С поле Владелец часто имеет составной тип (например, СправочникСсылка.Организации или СправочникСсылка.Контрагенты).
При работе через COM существуют нюансы:
БанковскиеСчета, но нет прав на справочник Контрагенты, то при попытке прочитать владельца-контрагента 1С вернет NULL из соображений безопасности.Nothing при определенных проверках. Рекомендуется использовать функцию ЕСТЬNULL или проверку на ЗначениеЗаполнено() на стороне запроса.
Проанализируем ситуацию с точки зрения производительности. Каждый вызов cursor.Следующий() и каждое обращение к полю через COM — это отдельный сетевой вызов между вашим приложением и сервером 1С. Если в выборке 1000 строк и 10 колонок, вы совершаете 10 000 микро-транзакций, что крайне медленно.
Рассмотрим более современный подход:
Это полностью снимает проблему конфликта имен (так как вы работаете со строковыми ключами JSON) и ускоряет обмен данными в десятки раз.
Подведем итог. Если вы столкнулись с тем, что поле Владелец возвращает NULL при COM-запросе, выполните следующие шаги:
ВладелецСчета).ЕСТЬNULL в запросе для обработки отсутствующих данных.Эти действия помогут вам избежать скрытых ошибок позднего связывания и обеспечат стабильную работу интеграции между вашим приложением и 1С.