Интеграция приложений на C# с платформой 1С:Предприятие через COM-соединение — это классический сценарий взаимодействия систем. Однако при реализации этой схемы разработчики часто сталкиваются со специфическими ошибками, связанными с различиями в объектных моделях .NET и 1С. Типичный пример — попытка программно провести документ, которая завершается ошибками выполнения вроде "System.__ComObject" не содержит определения для "Записать" или некорректной передачей параметров проведения.
В этой статье мы подробно разберем, почему возникают такие проблемы, как правильно переводить ссылки на документы в объекты для записи и как безопасно передавать системные перечисления 1С через границу COM-интерфейса.
Разберем ситуацию, когда нам необходимо найти документ в базе данных 1С и перепровести его. Первой типичной ошибкой является попытка вызвать метод Записать() непосредственно у объекта, полученного в результате поиска (например, через НайтиПоНомеру()).
Выясним причину этой ошибки. В объектной модели 1С все данные разделены на ссылки и объекты:
ДокументСсылка.УстановкаЦенНоменклатуры): Это неизменяемый указатель на запись в базе данных. Он содержит уникальный идентификатор (GUID) и позволяет считывать реквизиты документа (номер, дату, комментарий, контрагента). Однако ссылка не находится в оперативной памяти как изменяемая сущность, поэтому у неё физически нет методов для изменения или записи данных. Попытка вызвать метод Записать() у ссылки из C# приведет к исключению, так как COM-объект ссылки не содержит такого определения.ДокументОбъект.УстановкаЦенНоменклатуры): Это представление документа в оперативной памяти, предназначенное для редактирования, заполнения, записи и проведения. Именно у объекта существуют методы Записать(), Заполнить(), а также доступ к табличным частям на запись.Для того чтобы перейти от ссылки к объекту, в 1С используется метод ПолучитьОбъект(). Рассмотрим, как это выглядит на практике.
Вторая сложность заключается в передаче параметров в метод Записать(). Встроенный язык 1С для проведения документа требует передачи специального системного перечисления: РежимЗаписиДокумента.Проведение. Разработчики, не знакомые с COM-клиентом 1С, часто пытаются передать этот параметр в виде строки:
order.Записать("РежимЗаписиДокумента.Проведение");
Такой код вызовет ошибку при выполнении. Дело в том, что C# и среда выполнения .NET не могут неявно преобразовать текстовую строку в специфический внутренний тип данных 1С. Все системные перечисления платформы 1С при работе через COM должны извлекаться непосредственно из самого COM-объекта соединения.
Проанализируем правильный алгоритм работы с COM-соединением 1С для поиска и успешного проведения документа. Мы инициализируем подключение, находим нужную ссылку, преобразуем её в объект и вызываем проведение, используя верный формат передачи перечисления.
Ниже представлен готовый пример кода на C#:
// Инициализируем COM-соединение (в примере используется версия платформы 8.2/8.3)
COMConnector com1s = new V82.COMConnector();
com1s.PoolCapacity = 10;
com1s.PoolTimeout = 60;
com1s.MaxConnections = 2;
// Устанавливаем подключение к базе данных (строка подключения ConV82 настраивается заранее)
dynamic connection = com1s.Connect(ConV82);
// Шаг 1: Находим ссылку на документ по номеру и дате
dynamic refer = connection.Документы.УстановкаЦенНоменклатуры.НайтиПоНомеру("00000011275", "05.12.2018 17:38:56");
// Проверяем, что ссылка не пустая
if (refer != null && !connection.ЗначениеЗаполнено(refer))
{
Console.WriteLine("Документ не найден в базе данных.");
return;
}
Console.WriteLine("Успешно найдена ссылка на документ №: " + refer.Номер);
// Шаг 2: Получаем изменяемый объект документа из ссылки
dynamic order = refer.ПолучитьОбъект();
// Шаг 3: Проводим документ, передавая перечисление из контекста COM-подключения
order.Записать(connection.РежимЗаписиДокумента.Проведение);
Console.WriteLine("Документ успешно перепроведен!");
В этом примере мы обращаемся к свойству connection.РежимЗаписиДокумента.Проведение. Это позволяет передать на сторону 1С легитимный COM-объект перечисления, который платформа успешно распознает.
Хотя предложенное решение полностью закрывает локальную задачу перепроведения, при построении промышленных интеграционных решений на базе COM-технологий важно учитывать несколько неочевидных проблем:
comcntr.dll, обеспечивающий работу COMConnector, привязан к разрядности установленной платформы 1С. Если ваше .NET-приложение работает в 64-битном режиме, а на сервере установлена 32-битная версия клиента 1С (или наоборот), вы получите системную ошибку "Класс не зарегистрирован". Для решения этой проблемы часто приходится настраивать выделенный COM+ компонент в операционной системе Windows (внутри процесса dllhost.exe).Marshal.ReleaseComObject(connection).Записать() выбросит в C# общее исключение TargetInvocationException. Чтобы понять реальную причину отказа, вам придется оборачивать вызов в блок try-catch и анализировать системное описание ошибки через объект ИнформацияОбОшибке() на стороне 1С.Использование COM-соединения постепенно уходит в прошлое. Оно требует обязательного наличия установленной платформы 1С на клиентской машине, где запускается программа C#. Рассмотрим два более современных и надежных архитектурных подхода.
Платформа 1С поддерживает автоматическую публикацию базы данных в формате стандартного интерфейса OData. В этом случае взаимодействие происходит по протоколу HTTP/HTTPS, что позволяет отправлять запросы из C#-приложений, развернутых в контейнерах Docker или под управлением Linux.
Для проведения документа через OData мы можем отправить POST-запрос на адрес публикации:
POST http://your-server/your-base/odata/standard.odata/Document_УстановкаЦенНоменклатуры(guid'идентификатор_документа')/Post?PostingModeOperational=false
Параметр PostingModeOperational=false сообщает системе, что документ необходимо провести в неоперативном режиме (задним числом). Это избавляет от необходимости писать сложный код управления транзакциями на стороне C#.
Наиболее гибкий и рекомендуемый архитектурный подход — перенос всей бизнес-логики на сторону 1С. Вместо того чтобы манипулировать объектами 1С из внешнего кода, мы можем создать в конфигурации 1С собственный HTTP-сервис. Например, он будет принимать JSON с номером и датой документа.
В этом случае код на стороне 1С выполнит всю необходимую работу:
Функция ПровестиДокумент(Запрос)
// Разбираем JSON, находим документ
Ссылка = Документы.УстановкаЦенНоменклатуры.НайтиПоНомеру(НомерИзЗапроса, ДатаИзЗапроса);
Если ЗначениеЗаполнено(Ссылка) Тогда
Попытка
Объект = Ссылка.ПолучитьОбъект();
Объект.Записать(РежимЗаписиДокумента.Проведение);
Возврат Новый HTTPСервисОтвет(200); // Успешно
Исключение
// Возвращаем текст ошибки проведения
Возврат СформироватьОтветОбОшибке(ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецФункции
На стороне C# при таком подходе остается лишь отправить простой HTTP-запрос через стандартный класс HttpClient. Это разделяет зоны ответственности разработчиков и упрощает поддержку кода в будущем.