Как передать параметр перечисления через COM-соединение в запрос 1С?

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

При работе с внешними базами данных 1С через механизм COM-соединения разработчики часто сталкиваются со специфической проблемой: попытка передать значение перечисления в качестве параметра запроса во внешнюю базу вызывает ошибку или возвращает Неопределено. В этой статье мы подробно разберем, почему возникает эта проблема, и рассмотрим несколько надежных и эффективных способов её решения.

Почему стандартное обращение через COM не всегда работает?

В процессе работы внутри одной базы данных 1С мы привыкли обращаться к перечислениям напрямую через менеджер, например: Перечисления.ЮрФизЛицо.ФизическоеЛицо. Однако при попытке динамического обращения к коллекции внешней базы по строковому имени через квадратные скобки (например, Подключение.Перечисления[ИмяПеречисления][ИмяЗначения]) технология COM (а именно интерфейс IDispatch) может повести себя непредсказуемо.

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

Рассмотрим по шагам основные способы решения этой задачи, начиная от самых современных и рекомендуемых до альтернативных вариантов, которые выручат в сложных ситуациях.

Способ 1. Использование метода "ПредопределенноеЗначение" (Рекомендуемый)

Начиная с версии платформы 1С:Предприятие 8.2.9, разработчикам доступен крайне удобный метод ПредопределенноеЗначение(). Этот метод позволяет получить ссылку на предопределенный элемент (включая значения перечислений) по его строковому пути. При работе через COM-соединение этот метод вызывается у объекта подключения и работает исключительно быстро и стабильно.

Рассмотрим пример кода, реализующий данный подход:


// Предположим, что у нас есть локальная переменная "Субконто2", содержащая ссылку на перечисление
ИмяПеречисления = Субконто2.Метаданные().Имя;
ИндексЗначения = Перечисления[ИмяПеречисления].Индекс(Субконто2);
ИмяЗначения = Метаданные.Перечисления[ИмяПеречисления].ЗначенияПеречисления[ИндексЗначения].Имя;

// Формируем строковый путь к значению во внешней базе
ПутьКЗначению = "Перечисление." + ИмяПеречисления + "." + ИмяЗначения;

// Получаем COM-объект ссылки перечисления из внешней базы
ЗначениеCOM = Подключение.ПредопределенноеЗначение(ПутьКЗначению);

// Устанавливаем полученный COM-объект в параметры запроса
Запрос.УстановитьПараметр("Субконто2", ЗначениеCOM);

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

Способ 2. Получение значения по индексу через метод "Получить"

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

Давайте проанализируем, как это выглядит на практике:


ИмяПеречисления = Субконто2.Метаданные().Имя;

// Находим порядковый индекс значения в текущей базе (индексация начинается с 0)
ИндексЗначения = Перечисления[ИмяПеречисления].Индекс(Субконто2);

// Безопасно получаем аналогичное значение во внешней базе по индексу
ЗначениеCOM = Подключение.Перечисления[ИмяПеречисления].Получить(ИндексЗначения);

Запрос.УстановитьПараметр("Субконто2", ЗначениеCOM);

Важное замечание: Этот способ отлично работает только тогда, когда структуры перечислений в обеих базах строго синхронизированы. Если во внешней базе порядок значений перечисления отличается, данный метод вернет некорректное значение.

Способ 3. Сериализация и десериализация через XML

Один из самых надежных способов передачи ссылочных типов данных через границы программных систем — это сериализация. В платформе 1С для этого встроены механизмы работы с XML. Мы можем преобразовать локальное значение перечисления в XML-строку (которая по сути является системным именем значения), а на стороне COM-базы восстановить объект из этой строки.

Разберем этот процесс по шагам:


ИмяПеречисления = Субконто2.Метаданные().Имя;

// Превращаем локальную ссылку в XML-строку (например, "ФизическоеЛицо")
ИмяЗначенияXML = XMLСтрока(Субконто2);

// Во внешней базе получаем тип перечисления, которое мы хотим воссоздать
ТипПеречисленияCOM = Подключение.Type("ПеречислениеСсылка." + ИмяПеречисления);

// Десериализуем значение во внешней базе
ЗначениеCOM = Подключение.XMLЗначение(ТипПеречисленияCOM, ИмяЗначенияXML);

Запрос.УстановитьПараметр("Субконто2", ЗначениеCOM);

Почему это работает? Метод XMLЗначение() поддерживается платформой на уровне ядра и гарантирует точное воссоздание COM-объекта нужного типа на основе строкового идентификатора, избегая при этом прямых обращений к коллекциям метаданных через интерфейс COM.

Способ 4. Сравнение по порядку (индексу) внутри самого запроса

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

Для этого в тексте запроса внешней базы нам потребуется использовать свойство Порядок. Ниже приведен пример реализации:

Код на встроенном языке:


ИмяПеречисления = Субконто2.MetaДанные().Имя;
ИндексЗначения = Перечисления[ИмяПеречисления].Индекс(Субконто2);

// Передаем обычное число во внешнюю базу
Запрос.УстановитьПараметр("ИндексПараметра", ИндексЗначения);

Текст запроса во внешней базе:


ВЫБРАТЬ
    Взаиморасчеты.Договор,
    Взаиморасчеты.СуммаОстаток
ИЗ
    РегистрНакопления.Взаиморасчеты.Остатки КАК Взаиморасчеты
ГДЕ
    ВЫРАЗИТЬ(Взаиморасчеты.Субконто2 КАК Перечисление.ЮрФизЛицо).Порядок = &ИндексПараметра

Обратите внимание: Использование конструкции ВЫРАЗИТЬ(...) в запросе обязательно, так как поле субконто обычно имеет составной тип данных, и обратиться напрямую к свойству Порядок без явного приведения типа платформа не позволит.

Способ 5. Сравнение по строковому представлению в запросе

Еще один альтернативный способ, не требующий передачи COM-объектов — сравнение по строковому представлению. Мы можем передать в запрос обычную строчку (синоним значения перечисления) и сравнить её с помощью функции ПРЕДСТАВЛЕНИЕ.

Код подготовки параметра:


// Передаем строковое представление значения (например, "Физическое лицо")
Запрос.УстановитьПараметр("Субконто2Строкой", Строка(Субконто2));

Фрагмент запроса:

ГДЕ ПРЕДСТАВЛЕНИЕ(Таблица.Субконто2) = &Субконто2Строкой

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

Резюме и рекомендации

Подводя итоги, можно выделить следующие правила выбора решения:

  1. Если вы работаете на современных версиях платформы (8.3 и выше), всегда отдавайте предпочтение Способу 1 (ПредопределенноеЗначение). Это стандартный, быстрый и наиболее "чистый" с точки зрения архитектуры 1С подход.
  2. Если по каким-то причинам структуры баз динамически меняются или вам нужен универсальный механизм обмена данными, используйте Способ 3 (XML-сериализация).
  3. Для высоконагруженных систем, где важна максимальная скорость выполнения запросов без накладных расходов на передачу COM-объектов, используйте сравнение по порядку (Способ 4).
← На главную