Как работать с табличными частями 1С через COM Connector в C#?

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

Интеграция между 1С и внешними приложениями на языке C# часто реализуется через технологию COM Connector (V83.ComConnector). Одной из наиболее трудоемких задач в этом процессе является извлечение данных из табличных частей документов или справочников. Рассмотрим подробно, как правильно организовать этот процесс, избежать типичных ошибок с типами данных и оптимизировать производительность вашего решения.

Проблема доступа к табличной части через COM

При выполнении запроса в 1С через COM Connector, если в тексте запроса указана выборка всей ссылки или обращение к полю табличной части, C# получает объект типа __ComObject. Попытка простого перебора такой коллекции (например, через foreach) в .NET обычно приводит к ошибке "IEnumerable не реализован", так как COM-объекты 1С не поддерживают стандартные интерфейсы коллекций .NET напрямую.

Рассмотрим ситуацию: мы получили объект запроса и пытаемся прочитать табличную часть ВопросыОбращения из справочника ВходящиеДокументы. Типичный код на C# с использованием dynamic выглядит так:


var query = comConnector.НовыйОбъект("Запрос");
query.Текст = "ВЫБРАТЬ Ссылка, ДатаСоздания, Комментарий, ВопросыОбращения ИЗ Справочник.ВходящиеДокументы";
var queryResult = query.Выполнить().Выбрать();

while(queryResult.Следующий())
{
    var date = queryResult.ДатаСоздания;
    var questions = queryResult.ВопросыОбращения; // Здесь мы получаем COM-объект табличной части
    // Что делать дальше?
}

Решение 1: Использование метода Select() и англоязычного синтаксиса

Самый простой способ получить данные из табличной части — вызвать метод Выбрать(). Однако при работе через COM Connector в C# рекомендуется использовать англоязычные аналоги методов. Это связано с тем, что динамическая привязка (dynamic) в .NET иногда некорректно обрабатывает кириллические имена методов COM-сервера.

Проанализируем исправленный цикл перебора:


while(queryResult.Next()) // Используем Next() вместо Следующий()
{
    var questions = queryResult.ВопросыОбращения;
    var selection = questions.Select(); // Вызываем метод Select (Выбрать) у объекта табличной части

    while(selection.Next())
    {
        // Теперь мы можем обращаться к полям конкретной строки табличной части
        var subject = selection.ТемаВопроса; 
        var text = selection.ТекстВопроса;
    }
}

Важный нюанс: Метод Select() возвращает объект выборки (Selection), по которому можно перемещаться только вперед с помощью Next().

Решение 2: Выгрузка в Таблицу Значений (ValueTable)

Если вам необходимо знать количество строк в табличной части или обращаться к строкам по индексу, лучше использовать метод Unload() (Выгрузить). Он превращает табличную часть в объект ValueTable (ТаблицаЗначений), который обладает более гибким интерфейсом.

Разберем этот подход по шагам:

  1. Выгружаем табличную часть в ValueTable.
  2. Используем метод Count() (Количество), чтобы определить размер коллекции.
  3. Используем метод Get(index) (Получить), чтобы извлечь конкретную строку по ее порядковому номеру.

var questionsTable = queryResult.ВопросыОбращения.Unload(); // Выгружаем в таблицу значений
int count = (int)questionsTable.Count();

for (int i = 0; i < count; i++)
{
    var row = questionsTable.Get(i); // Получаем строку по индексу
    var questionValue = row.ТекстВопроса;
    Console.WriteLine($"Строка {i}: {questionValue}");
}

Оптимизация запроса: Избавляемся от вложенных обращений

С точки зрения производительности 1С, обращение queryResult.ВопросыОбращения внутри цикла является "тяжелой" операцией. Каждый раз, когда вы обращаетесь к этому свойству, система выполняет неявный дополнительный запрос к базе данных, чтобы подтянуть данные табличной части для конкретного объекта.

Проанализируем ситуацию: если у вас 1000 документов, вы выполните 1 основной запрос и 1000 дополнительных. Это значительно замедляет работу. Рассмотрим более эффективный способ — плоский запрос.

Вместо того чтобы выбирать документ и лезть в его свойства, выберем сразу таблицу строк:


query.Текст = @"ВЫБРАТЬ
                Входящие.Ссылка КАК ДокументСсылка,
                Входящие.ДатаСоздания,
                Вопросы.ТекстВопроса,
                Вопросы.НомерСтроки
              ИЗ
                Справочник.ВходящиеДокументы КАК Входящие
              ЛЕВОЕ СОЕДИНЕНИЕ 
                Справочник.ВходящиеДокументы.ВопросыОбращения КАК Вопросы
              ПО 
                Вопросы.Ссылка = Входящие.Ссылка";

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

Критический аспект: Управление памятью и Marshal.ReleaseComObject

Работа с COM Connector в .NET коварна тем, что среда исполнения (CLR) не знает, когда освобождать ресурсы 1С. Каждая переменная queryResult, questions, row — это отдельный COM-объект. Если их не освобождать вручную, процесс dllhost.exe на сервере будет бесконечно раздуваться в памяти, что приведет к падению системы.

Правильный шаблон работы должен выглядеть так:


dynamic questions = null;
dynamic selection = null;
try 
{
    questions = queryResult.ВопросыОбращения;
    selection = questions.Select();
    while(selection.Next())
    {
        // Логика обработки
    }
}
finally 
{
    if (selection != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(selection);
    if (questions != null) System.Runtime.InteropServices.Marshal.ReleaseComObject(questions);
}

Запомните: Всегда используйте try-finally и освобождайте каждый объект 1С, полученный через точку или метод.

Обработка типов данных (Маршалинг)

При получении значений из 1С в C# важно учитывать следующие моменты:

Современная альтернатива: REST OData

Если ваша конфигурация 1С работает на платформе 8.3, рассмотрите возможность отказа от COM в пользу OData (REST API). В 1С:Документооборот этот интерфейс включается одной галочкой в публикации. Для C# разработчика это гораздо удобнее:

  1. Данные приходят в формате JSON.
  2. Табличные части представлены как вложенные массивы объектов.
  3. Не нужно заботиться об установке компонент 1С на клиентскую машину и разрядности системы (x86/x64).
  4. Никаких утечек памяти, связанных с COM-объектами.

Подводя итог, работа через COM Connector остается актуальной для старых систем (например, УПП), но требует дисциплины в именовании методов (английский язык) и строгого контроля за освобождением ресурсов через Marshal.ReleaseComObject.

← На главную