В практике разработки на платформе 1С:Предприятие иногда возникают ситуации, которые кажутся невозможными с точки зрения логики работы базы данных. Одна из самых загадочных проблем — когда две ссылки на один и тот же объект (например, элемент справочника Номенклатура) имеют абсолютно идентичные Уникальные Идентификаторы (GUID), их сериализованное представление в XML полностью совпадает, но при этом обычное сравнение Ссылка1 = Ссылка2 возвращает Ложь. Более того, поиск такой ссылки в таблице значений методом Найти() выдает пустой результат. Рассмотрим подробно, почему это происходит и как с этим бороться.
Прежде чем переходить к решению, давайте разберем типичную картину «болезни» системы, с которой сталкиваются разработчики. Представим ситуацию: у нас есть объект, полученный в результате обмена или сложной выборки. Мы выполняем ряд проверок, чтобы понять, почему система не видит равенства:
Ссылка1.УникальныйИдентификатор() = Ссылка2.УникальныйИдентификатор() возвращает Истина. Это говорит о том, что физически данные указывают на одну и ту же запись в таблице СУБД.ТипЗнч(Ссылка1) = ТипЗнч(Ссылка2) возвращает Истина. Объекты принадлежат одному и тому же метаданному.ЗначениеВСтрокуВнутр() показывает одинаковые значения.Несмотря на все эти «за», поиск в таблице значений (ТЗ) или простое равенство не срабатывают. Проанализируем ситуацию: почему платформа считает их разными?
Как показывает практика и опыт решения подобных кейсов, основной виновник — некорректное завершение транзакции. В архитектуре 1С ссылки — это не просто GUID. На уровне сервера приложений ссылка представляет собой сложный дескриптор, который включает в себя информацию о состоянии объекта в текущем сеансе и его связи с контекстом базы данных.
Разберем по шагам, что происходит при сбое транзакции:
Номенклатура).ПередЗаписью), но транзакция не была явно откачена (ОтменитьТранзакцию()) или осталась «висеть» из-за особенностей обработки исключений, созданная ссылка в памяти сервера переходит в неопределенное состояние.В этом случае мы получаем две ссылки, которые указывают на один УИД, но имеют разные внутренние указатели в памяти рабочего процесса rphost. Для платформы это разные «объекты-ссылки», и их прямое сравнение будет давать Ложь.
Платформа 1С активно использует кэширование для ускорения работы. Когда мы обращаемся к объекту, сервер кэширует его состояние. Если в системе произошел программный сбой, связанный с блокировками или транзакциями, данные в кэше могут десинхронизироваться с реальными данными в СУБД.
Выясним причину, по которой поиск в ТаблицеЗначений выдает пустой результат. Метод Найти() и работа индексов в ТЗ основаны на хэшировании. Хэш-код ссылки вычисляется не только по GUID, но и по внутреннему состоянию объекта ссылки в памяти. Если «грязная» ссылка попала в таблицу до того, как транзакция «стабилизировалась», ее хэш будет отличаться от хэша «чистой» ссылки, полученной после завершения всех операций. В итоге — визуально данные одинаковы, но индекс их не находит.
Чтобы убедиться, что проблема именно в этом, проанализируем ситуацию с помощью следующих инструментов. Попробуйте выполнить сравнение не только самих ссылок, но и их внутренних свойств:
// Проверка типов и внутренних представлений
Если ТипЗнч(СтрокаТЧ.Номенклатура) = ТипЗнч(ТаблицаВычерков[0].Номенклатура) Тогда
Сообщить("Типы совпадают");
КонецЕсли;
Если ЗначениеВСтрокуВнутр(СтрокаТЧ.Номенклатура) = ЗначениеВСтрокуВнутр(ТаблицаВычерков[0].Номенклатура) Тогда
Сообщить("Внутренние строки совпадают");
КонецЕсли;
// Если все выше Истина, но это Ложь - значит мы имеем дело с проблемой транзакционного кэша
Если СтрокаТЧ.Номенклатура <> ТаблицаВычерков[0].Номенклатура Тогда
Сообщить("ВНИМАНИЕ: Ссылки не равны при одинаковых данных!");
КонецЕсли;
Рассмотрим подробнее шаги, которые необходимо предпринять для устранения этой ошибки.
Это самое важное решение. Убедитесь, что все операции записи обернуты в блоки Попытка...Исключение с обязательным откатом транзакции. Если транзакция не завершена корректно, ссылки в памяти сеанса будут «отравлены».
НачатьТранзакцию();
Попытка
Объект = Ссылка.ПолучитьОбъект();
// Логика обработки
Объект.Записать();
ЗафиксироватьТранзакцию();
Исключение
// Если произошла ошибка, транзакция ДОЛЖНА быть отменена
Если ТранзакцияАктивна() Тогда
ОтменитьТранзакцию();
КонецЕсли;
ЗаписатьЛогОшибки(ОписаниеОшибки());
КонецПопытки;
Если проблема уже возникла и ссылки «разошлись» в памяти, иногда помогает только принудительная очистка состояния. Рассмотрим варианты:
rphost..dt и обратная загрузка для пересчета индексов.Проанализируем ситуацию при обмене данными. Часто разработчики используют УстановитьСсылкуНового(), чтобы сохранить GUID из другой системы. Если после установки ссылки объект не был записан из-за ошибки, а код продолжает использовать эту ссылку, она превращается в «фантом». При последующих поисках такая ссылка будет отличаться от реально записанной в базу, даже если GUID совпадут. Всегда проверяйте успешность записи перед помещением ссылки в коллекции для поиска.
Помните, что Уникальный Идентификатор уникален только внутри конкретной таблицы метаданных. В теории (хотя на практике это случается крайне редко при программных ошибках или сбоях РИБ) в базе могут существовать два разных объекта разных типов (например, Справочник.Номенклатура и Справочник.Контрагенты) с одинаковым GUID. При сравнении их ссылок результат всегда будет Ложь. Поэтому всегда проверяйте равенство типов через ТипЗнч().
Если вы столкнулись с тем, что 1С «не видит» одинаковые объекты:
ПолучитьОбъект() = ПолучитьОбъект() всегда Ложь).ЗначениеВСтрокуВнутр() для глубокой диагностики различий в дескрипторах.Соблюдение дисциплины работы с транзакциями гарантирует стабильность ссылочной целостности и предсказуемое поведение механизмов поиска в коллекциях 1С.