Многие разработчики сталкиваются с типичной проблемой при интеграции 1С с внешними базами данных, такими как Firebird или InterBase. Первое подключение к файлу базы данных проходит успешно, данные считываются, но при попытке повторного подключения или выполнения нового запроса возникает ошибка: Произошла исключительная ситуация (ADODB.Recordset): Аргументы имеют неверный тип, выходят за пределы допустимого диапазона или вступают в конфликт друг с другом. Иногда эта ошибка может звучать иначе: Операция не допускается, если объект закрыт.
Давайте подробно разберем, почему возникает эта проблема и как ее правильно решить, чтобы обеспечить стабильную работу вашей интеграции.
Основная причина кроется во взаимодействии платформы 1С, технологии ADO (ActiveX Data Objects) и драйверов баз данных. Рассмотрим основные факторы:
Close() и присваиваем переменной соединения значение Неопределено, физического разрыва связи с БД часто не происходит. Соединение возвращается в пул. При повторном обращении 1С достает из пула «полузакрытое» соединение, внутреннее состояние которого рассинхронизировано с драйвером. Это и вызывает конфликт типов аргументов или ошибку закрытого объекта при попытке инициализировать новый ADODB.Recordset.Неопределено не всегда приводит к мгновенной сборке мусора и удалению объекта из памяти платформы. Если старый объект набора записей или незакрытая транзакция продолжают «висеть» в памяти рабочего процесса, новое подключение неизбежно вступит с ними в конфликт.Вместо того чтобы передавать текст запроса напрямую в метод открытия рекордсета, давайте перепишем код с использованием промежуточного объекта ADODB.Command. Также перед открытием соединения зададим дополнительные параметры курсора, таймаута и режима работы. Посмотрим на пример правильной инициализации:
// 1. Настраиваем и открываем соединение
Connection = Новый COMОбъект("ADODB.Connection");
Connection.ConnectionString = "Driver=Firebird/InterBase(r) driver;" +
"Dbname=" + СокрЛП(Объект.ПутьКФайлу) + ";" +
"UID=" + СокрЛП(Объект.Пользователь) + ";" +
"PWD=" + СокрЛП(Объект.Пароль);
Connection.ConnectionTimeOut = 20;
Connection.CursorLocation = 1;
Connection.Mode = 1;
Попытка
Connection.Open();
Сообщить("Успешное подключение");
Исключение
Сообщить("Подключение к БД не установлено. Ошибка: " + ОписаниеОшибки());
Connection = Неопределено;
Возврат;
КонецПопытки;
// 2. Используем ADODB.Command для выполнения запроса
Команда = Новый COMОбъект("ADODB.Command");
Команда.ActiveConnection = Connection;
Команда.CommandText = "Select produce.id From produce Where produce.isactive = 1";
НаборЗаписей = Новый COMОбъект("ADODB.RecordSet");
Попытка
НаборЗаписей = Команда.Execute();
// Обход результатов выборки...
Исключение
ЗаписьЖурналаРегистрации("ВнешняяБД.ОшибкаЗапроса", УровеньЖурналаРегистрации.Ошибка,,, Команда.CommandText + Символы.ПС + ОписаниеОшибки());
КонецПопытки;
Чтобы заставить ADO каждый раз создавать новое, гарантированно «чистое» физическое соединение, мы можем отключить кэширование на уровне драйвера. Для этого добавим в строку подключения (в свойство ConnectionString) специальные параметры. В зависимости от версии и типа используемого драйвера, попробуйте добавить параметр Pooling=False или OLE DB Services = -2 (а в некоторых случаях -4). Это заставит подсистему принудительно обрывать физическую связь при вызове метода закрытия.
Посмотрим на то, как мы завершаем работу с объектами ADO. Перед закрытием объекта Recordset крайне полезно принудительно отвязать его от текущего соединения. Присвоим свойству ActiveConnection значение Неопределено (или Null), и только после этого вызовем метод Close().
Также важно помнить о механизме транзакций. Если драйвер неявно открывает транзакцию (например, при чтении данных с определенным уровнем изоляции), ее нужно явно завершить, вызвав методы Commit или Rollback у объекта Connection. Соединение с незавершенной транзакцией, вернувшееся в пул, гарантированно вызовет ошибку при попытке повторного использования в следующем цикле работы 1С.
Разберем наиболее правильные архитектурные подходы, которые позволят избежать проблемы в корне:
ADODB.Connection внутри цикла или при каждом отдельном мелком запросе. Установите соединение один раз (например, при открытии формы или старте обработки), сохраните его в переменную модуля, выполняйте через него все необходимые операции и закрывайте только при полном окончании работы с формой.Применив один или несколько из описанных подходов, вы сможете обеспечить стабильный и бесперебойный обмен данными между 1С и внешними базами.