Каждый начинающий разработчик в среде 1С:Предприятие рано или поздно сталкивается с необъяснимым поведением платформы во время отладки кода. Одной из таких загадочных ситуаций является "самопроизвольный" переход выборки запроса на следующую строку. Вы только что получили выборку, остановились на точке останова перед циклом, но при просмотре значений переменных обнаруживаете, что указатель выборки уже сдвинулся вперед, а данные начали обрабатываться в обход написанного вами алгоритма.
В этой статье мы подробно разберем, почему возникает этот эффект, как устроен механизм вычисления выражений в отладчике 1С, к каким еще скрытым побочным эффектам он может привести и как правильно и безопасно анализировать данные в процессе разработки.
Рассмотрим классическую ситуацию. Разработчик пишет код для обработки результатов запроса:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Здесь должна происходить обработка данных
Сотрудник = Выборка.Сотрудник;
КонецЦикла;
При запуске отладки и остановке на строке Выборка = Запрос.Выполнить().Выбрать(); программист хочет заранее посмотреть, какие данные лежат внутри выборки. Он открывает окно Табло (сочетание клавиш Ctrl + Alt + W) или использует окно Вычислить выражение (Shift + F9) и вводит туда команду: Выборка.Следующий() или пытается напрямую прочитать свойства выборки, которые еще не доступны, так как метод позиционирования не был вызван.
После этого начинается «мистика»: при пошаговом переходе (клавиша F10) строки в выборке начинают переключаться сами по себе, даже если выполнение кода еще не дошло до строки Пока Выборка.Следующий() Цикл. В итоге реальный цикл пропускает часть записей или вовсе не выполняется, так как выборка уже была полностью прочитана в процессе обхода по шагам.
Для того чтобы понять, почему так происходит, проанализируем внутренние механизмы работы конфигуратора 1С:
Выборка.Следующий(), то при каждом нажатии клавиш F10 (Шагнуть через) или F11 (Шагнуть в) отладчик неявно выполняет этот метод, чтобы обновить значение в таблице отображения.Следующий() у объекта РезультатЗапросаВыборка является функцией с побочным эффектом. Он не просто возвращает булево значение (Истина/Ложь), но и физически сдвигает указатель текущей строки выборки на одну позицию вперед. Поскольку отладчик выполняет эту команду за кулисами для обновления интерфейса «Табло», выборка незаметно для разработчика продвигается к концу.Таким образом, пока вы пошагово анализируете код, отладчик самостоятельно прогоняет выборку до самого конца, из-за чего основной алгоритм программы лишается данных для обработки.
Решение этой проблемы предельно простое, но крайне важное для понимания:
Следующий(), Прочитать(), Записать(), ПолучитьОбъект()), из окон «Табло» и «Быстрый просмотр».Запрос.Выполнить().Выгрузить()Побочные эффекты вычисления выражений в отладчике могут приводить и к более серьезным ошибкам, которые сложно локализовать. Рассмотрим ситуацию с использованием временных таблиц.
Если в вашем коде используется МенеджерВременныхТаблиц, и вы решили выполнить запрос целиком в окне «Вычислить выражение» до того, как код выполнится в программе, вы рискуете получить ошибку времени выполнения: «Таблица с таким именем уже существует».
Это происходит потому, что при первом (ручном) вычислении запроса в отладчике временная таблица уже была создана и помещена в менеджер в текущем сеансе. Когда программа доходит до реального шага выполнения этого же запроса, платформа пытается создать таблицу повторно с тем же именем в рамках того же менеджера, что приводит к аварийному завершению работы системы.
Как правильно смотреть временные таблицы в отладчике:
Начиная с современных версий платформы 1С v8.3, нет необходимости повторно выполнять запросы для просмотра содержимого временных таблиц. Если запрос уже выполнился в коде, вы можете обратиться напрямую к менеджеру временных таблиц. Введите в «Вычислить выражение» следующую конструкцию:
Запрос.МенеджерВременныхТаблиц.Таблицы.Найти("ИмяВашейВременнойТаблицы").ПолучитьДанные().Выгрузить()
Этот метод абсолютно безопасен. Он не перезапускает создание таблиц, не дублирует данные и позволяет увидеть точное содержимое промежуточных таблиц в памяти СУБД на текущий момент отладки.
Проанализируем еще два критически важных аспекта, о которых должен знать каждый разработчик при работе с отладчиком:
Помните, что любые интерактивные вычисления функций, изменяющих базу данных, отрабатывают в реальности. Например, если вы введете в поле вычисления выражения следующий код:
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
НовыйЭлемент.Наименование = "Тестовый товар из отладки";
НовыйЭлемент.Записать();
Этот элемент действительно будет записан в вашу базу данных. Если вы проводите отладку на «живой» рабочей базе (что само по себе является плохой практикой), такие действия могут засорить систему мусорными данными или нарушить бизнес-логику.
Когда вы останавливаетесь на точке останова внутри транзакции (например, в процедуре ОбработкаПроведения документа), СУБД накладывает и удерживает блокировки на изменяемые таблицы базы данных.
Пока вы стоите на точке останова и неторопливо анализируете переменные или выполняете тяжелые сопутствующие запросы в табло, все остальные пользователи системы будут заблокированы при попытке обратиться к тем же таблицам. Это может привести к ошибкам превышения времени ожидания блокировки у пользователей (Lock Timeout) или к возникновению взаимных блокировок (Deadlocks).
Золотое правило разработчика: Никогда не задерживайтесь надолго на точках останова внутри транзакций на работающих базах данных и минимизируйте использование сложных вычислений в табло в этот момент.
Для того чтобы процесс отладки приносил только пользу и не вызывал появление седых волос, придерживайтесь следующих простых правил:
Следующий(), Получить(), Записать()). Используйте табло только для просмотра свойств переменных и констант.РезультатЗапроса.Выгрузить() в окне быстрого вычисления (Shift + F9).Понимание физики процессов, происходящих под капотом платформы 1С:Предприятие, убережет вас от ложных выводов о «багах платформы» и поможет писать чистый, предсказуемый и производительный код.