В практике разработки на платформе 1С:Предприятие 8.3 программисты иногда сталкиваются с ситуациями, которые кажутся магическими или необъяснимыми. Одна из таких проблем — разное поведение идентичного, на первый взгляд, кода при работе с наборами записей регистров сведений. Рассмотрим ситуацию, когда один и тот же запрос к базе данных в одном случае возвращает запись, а в другом — пустой набор, и разберемся, как это исправить.
Представим сценарий: мы работаем в контексте веб-сервиса или серверного модуля. Нам нужно проверить наличие записи в регистре по определенному идентификатору. Проанализируем два варианта кода, которые на логическом уровне должны давать одинаковый результат:
Вариант 1 (Прямое обращение):
НаборЗаписей = РегистрыСведений.ТестовыйРегистр.СоздатьНаборЗаписей();
НаборЗаписей.Отбор["айди_записи"].Установить("4124124");
НаборЗаписей.Прочитать();
// Результат: НаборЗаписей.Количество() = 1
Вариант 2 (Через промежуточную переменную):
Регистр = РегистрыСведений.ТестовыйРегистр;
НаборЗаписей = Регистр.СоздатьНаборЗаписей();
НаборЗаписей.Отбор["айди_записи"].Установить("4124124");
НаборЗаписей.Прочитать();
// Результат: НаборЗаписей.Количество() = 0
На первый взгляд, разницы быть не должно. Однако в некоторых случаях (особенно на платформе версии 8.3.24.1758) второй вариант может возвращать пустой результат, даже если запись физически существует в таблице базы данных. Давайте выясним, почему так происходит и какие факторы на это влияют.
Как показывает практика, наиболее вероятной причиной такого "мистического" поведения является физическое или логическое повреждение индексов таблицы регистра сведений. Платформа 1С при обращении к данным может строить разные планы выполнения запроса в зависимости от того, как инициализирован объект МенеджерРегистраСведений.
Если индекс по измерению айди_записи поврежден, один механизм доступа (например, прямой вызов метода через метаданные) может использовать кэшированные данные или другой индекс, в то время как обращение через переменную-ссылку может инициировать чтение через поврежденный индексный путь. Разберем, как это лечится:
Проанализируем контекст выполнения кода. Если наш алгоритм находится внутри функции веб-сервиса, он выполняется в сеансе фонового задания. В таких сессиях кэширование менеджеров объектов работает иначе, чем в обычном клиентском приложении.
При использовании конструкции Регистр = РегистрыСведений.ТестовыйРегистр; мы сохраняем ссылку на менеджер регистра в локальную переменную. В редких случаях на специфических сборках платформы (таких как 8.3.24) это может привести к тому, что созданный из этой переменной НаборЗаписей обращается к неактуальному состоянию кэша метаданных. В то же время прямое обращение через глобальную коллекцию РегистрыСведений заставляет платформу выполнить более "свежую" инициализацию объекта.
Посмотрим на чистоту кода. В 1С допускается вызов методов без параметров без использования круглых скобок, но это считается плохой практикой, особенно в серверном коде. Рассмотрим пример из вопроса автора:
НаборЗаписей = Регистр.СоздатьНаборЗаписей; // Плохо
НаборЗаписей.Прочитать; // Плохо
Если НаборЗаписей.Количество = 0 Тогда // Рискованно
Почему это важно? В объекте РегистрСведенийНаборЗаписей нет свойства Количество, но есть метод Количество(). Если написать НаборЗаписей.Количество без скобок, интерпретатор в определенных условиях (например, при конфликте имен в расширениях или специфическом контексте веб-сервиса) может некорректно разрешить этот вызов. Всегда используйте явный вызов функций:
НаборЗаписей = Регистр.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.айди_записи.Установить("4124124");
НаборЗаписей.Прочитать();
Если НаборЗаписей.Количество() = 0 Тогда
// Действия, если запись не найдена
КонецЕсли;
Также обратим внимание на переменную Регистр. Если в модуле или в параметрах функции уже существует переменная или реквизит с таким именем (например, в некоторых формах или внешних обработках), обращение к ней может вернуть не то, что вы ожидаете. Старайтесь использовать более уникальные имена переменных, например МенеджерТестовогоРегистра.
Если ваша задача — проверить наличие записи и, если ее нет, создать новую, проанализируем наиболее надежный способ с использованием МенеджерЗаписи. Это зачастую эффективнее, чем работа с целым набором, когда нам нужна всего одна строка:
Функция АктуализироватьДаннуюЗапись(Идентификатор)
Менеджер = РегистрыСведений.ТестовыйРегистр.СоздатьМенеджерЗаписи();
Менеджер.айди_записи = Идентификатор;
// Пытаемся прочитать существующую запись
Менеджер.Прочитать();
Если Не Менеджер.Выбран() Тогда
// Записи нет, заполняем ключевые поля и записываем
Менеджер.айди_записи = Идентификатор;
// Заполнение остальных ресурсов
Менеджер.Записать();
Возврат Истина;
КонецЕсли;
Возврат Ложь; // Запись уже была
КонецФункции
Если вы столкнулись с ситуацией, когда НаборЗаписей.Прочитать() не видит существующие данные в одном месте кода, но видит в другом:
СоздатьНаборЗаписей(), Прочитать(), Количество().Регистр системные поля или реквизиты объекта.Такой системный подход позволит избежать трудноуловимых ошибок и обеспечит стабильную работу ваших интеграций и веб-сервисов.