При разработке механизмов обмена данными или создании сложных алгоритмов генерации объектов в 1С:Предприятие, программисты часто сталкиваются с необходимостью принудительно назначить уникальный идентификатор (GUID) для нового объекта еще до его записи в базу данных. Для этих целей платформа предоставляет метод УстановитьСсылкуНового().
Однако, при попытке сразу же проверить результат присвоения через свойство Ссылка, мы получаем неожиданный результат: ссылка объекта остается пустой, а сравнение с назначенным значением возвращает Ложь. В этой статье мы подробно разберем, почему так происходит, как устроена архитектура объектной модели 1С в этом аспекте, и как правильно работать с ссылками новых объектов.
Рассмотрим ситуацию, которая вызывает недоумение. Мы создаем новый объект (например, элемента справочника или документа), генерируем для него новую уникальную ссылку и пытаемся установить её объекту.
Давайте взглянем на код, который демонстрирует проблему:
// Создаем новый объект (например, справочника)
НовыйОбъект = Справочники.Номенклатура.СоздатьЭлемент();
// Генерируем новый уникальный идентификатор (Ссылка)
НоваяСсылка = Справочники.Номенклатура.ПолучитьСсылку();
// Пытаемся установить эту ссылку нашему новому объекту
НовыйОбъект.УстановитьСсылкуНового(НоваяСсылка);
// ПРОВЕРКА
// Мы ожидаем, что НовыйОбъект.Ссылка теперь равна НоваяСсылка
РезультатСравнения = (НовыйОбъект.Ссылка = НоваяСсылка);
// Выводим результат
Сообщить("Равны ли ссылки? " + РезультатСравнения); // Выведет: ЛОЖЬ
Возникает закономерный вопрос: почему платформа игнорирует нашу команду? Мы ведь явно вызвали метод установки ссылки. Чтобы ответить на этот вопрос, нам необходимо углубиться в то, как платформа 1С управляет объектами в памяти и в базе данных.
Главная причина такого поведения кроется в природе свойства Ссылка у прикладных объектов (СправочникОбъект, ДокументОбъект и т.д.).
Важно понимать следующее фундаментальное правило платформы: свойство Ссылка у объекта, который еще не записан в базу данных, всегда возвращает Пустую Ссылку данного типа.
Давайте проанализируем это детально:
СоздатьЭлемент() или СоздатьДокумент()), он существует только в оперативной памяти сервера (или клиента, в зависимости от контекста). В таблицах базы данных записи еще нет.Объект.Ссылка — это по сути "проекция" физического существования записи в БД. Это не просто переменная, которую можно менять. Это идентификатор, под которым объект уже лежит в базе.Справочник.ИмяСправочника.ПустаяСсылка при обращении к Объект.Ссылка.Именно поэтому выражение НовыйОбъект.Ссылка = НоваяСсылка возвращает Ложь. Вы сравниваете Пустую Ссылку (которая хранится в свойстве незаписанного объекта) с Конкретной Ссылкой (которую вы сгенерировали и храните в переменной).
Если свойство Ссылка доступно только для чтения и отражает состояние базы данных, то что же делает метод УстановитьСсылкуНового()? Куда девается переданное нами значение?
Метод УстановитьСсылкуНового(<Ссылка>) работает как механизм отложенного назначения. Рассмотрим алгоритм его действия:
Ссылка. Вместо этого она сохраняет переданный GUID в специальную внутреннюю служебную переменную объекта (можно представить это как скрытый "карман" или "буфер").Объект.Записать(), платформа проверяет этот внутренний буфер.
УстановитьСсылкуНового): Платформа берет GUID из буфера и пытается выполнить команду INSERT в базу данных именно с этим идентификатором.Объект.Ссылка обновляется и начинает возвращать тот самый GUID, который мы установили ранее.Таким образом, УстановитьСсылкуНового — это своего рода "бронирование" идентификатора, который будет применен только в момент фактического рождения записи в базе данных.
Если Объект.Ссылка бесполезна до записи, как же нам убедиться, что мы всё сделали правильно и объект "запомнил" нужный идентификатор? Для этого существует парный метод — ПолучитьСсылкуНового().
Давайте посмотрим, как правильно работать с этими методами в коде:
НовСсылка = Справочники.Товары.ПолучитьСсылку();
ОбъектТовар = Справочники.Товары.СоздатьЭлемент();
// "Кладем" ссылку в буфер
ОбъектТовар.УстановитьСсылкуНового(НовСсылка);
// ПРОВЕРКА НЕПРАВИЛЬНАЯ
// ОбъектТовар.Ссылка все еще Пустая!
Если ОбъектТовар.Ссылка = НовСсылка Тогда
// Сюда мы не попадем
КонецЕсли;
// ПРОВЕРКА ПРАВИЛЬНАЯ
// "Достаем" ссылку из буфера для проверки
УстановленнаяСсылка = ОбъектТовар.ПолучитьСсылкуНового();
Если УстановленнаяСсылка = НовСсылка Тогда
Сообщить("Ура! Ссылка успешно назначена и будет применена при записи.");
КонецЕсли;
Метод ПолучитьСсылкуНового() возвращает именно то значение, которое было передано в УстановитьСсылкуНового(). Если метод установки не вызывался, функция вернет Неопределено (или пустую ссылку, в зависимости от контекста реализации, но чаще используется для проверки факта ручной установки).
Еще один важный аспект, который обсуждался в теме — гарантия уникальности. Почему платформа не присваивает ссылку сразу?
Дело в том, что уникальность ссылки проверяется только при записи объекта. Даже если вы сгенерировали GUID и установили его через УстановитьСсылкуНового, это не гарантирует, что в базе данных уже не существует объекта с таким же идентификатором (например, созданного в другой сессии или загруженного через обмен секундой ранее).
Если бы Объект.Ссылка присваивалась сразу, это вводило бы в заблуждение, что объект уникален и существует. Платформа придерживается строгой логики:
Записать() обнаружится дублирование ключа (GUID), платформа выбросит исключение (ошибку), и транзакция будет отменена.Рассмотрим сценарии, где использование связки ПолучитьСсылку() + УстановитьСсылкуНового() является обязательным.
Это самый частый случай. Когда вы загружаете данные из другой базы 1С или внешней системы, вам критически важно сохранить Уникальный Идентификатор (UUID) объекта, чтобы при повторной загрузке система нашла этот объект, а не создала дубль. Вы читаете UUID из файла, генерируете по нему ссылку и назначаете её новому объекту перед записью.
Представьте, что вам нужно создать документ "Заказ" и сразу же создать запись в регистре сведений или подчиненный справочник, который ссылается на этот "Заказ". Но "Заказ" еще не записан!
В этом случае вы:
СслыкаЗаказа = Документы.Заказ.ПолучитьСсылку();ОбъектЗаказ.УстановитьСсылкуНового(СсылкаЗаказа);СсылкаЗаказа для заполнения реквизитов в подчиненных объектах.Без этого механизма вам пришлось бы сначала записать Заказ (чтобы получить его ссылку), и только потом создавать подчиненные данные, что не всегда удобно или допустимо с точки зрения логики транзакции.
Подведем итог нашему исследованию:
НовыйОбъект.Ссылка = МояНоваяСсылка возвращает Ложь, потому что НовыйОбъект.Ссылка до момента записи всегда равна Пустой Ссылке.УстановитьСсылкуНового() сохраняет GUID во внутренний буфер объекта, не меняя его текущее свойство Ссылка.ПолучитьСсылкуНового().Записать().