При переходе на управляемый интерфейс 1С:Предприятия 8 многие разработчики сталкиваются с непониманием того, как устроен реквизит Объект на управляемой форме. Возникает естественный вопрос: почему для вызова метода документа или справочника недостаточно обратится к Объект.ИмяМетода(), а требуется использовать функцию РеквизитФормыВЗначение() или метод ПолучитьОбъект()?
В этой статье мы подробно разберем архитектуру работы с данными на управляемых формах, проанализируем разницу между прикладными объектами в памяти и легкими структурами данных формы, а также рассмотрим лучшие практики выполнения серверных операций.
Рассмотрим причину, по которой методы модуля объекта недоступны напрямую из реквизита формы. На клиенте и на сервере реквизит управляемой формы Объект имеет тип ДанныеФормыСтруктура, а не прикладной тип ДокументОбъект или СправочникОбъект.
Тип ДанныеФормыСтруктура представляет собой специальный «облегченный» контейнер данных, оптимизированный для быстрой передачи по сети между клиентом и сервером. В нем содержатся только значения реквизитов и табличных частей, необходимых для отображения в интерфейсе. Из-за такой оптимизации данный тип не содержит в себе:
ПроверитьЗаполнение(), Записать(), УстановитьПометкуУдаления()).ОбработкаПроведения()).Для выполнения бизнес-логики, заложенной в модуле объекта, несохраненные данные формы необходимо предварительно преобразовать в прикладной объект в памяти сервера.
Разберем классический вариант работы с данными формы на сервере. Когда нам необходимо применить алгоритмы модуля объекта к текущим данным, введенным пользователем на экране (даже если документ еще не записан в базу данных), мы используем следующий механизм:
&НаСервере
Процедура ВыполнитьДействиеНаСервере()
// Преобразуем данные формы в полноценный объект в памяти сервера
ДокументОбъект = РеквизитФормыВЗначение("Объект");
// Вызываем экспортный метод объекта или системную процедуру
ДокументОбъект.ЗаполнитьПоОстаткам();
// Возвращаем измененные данные обратно на форму
ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");
КонецПроцедуры
Выясним, что происходит при каждом шаге выполнения этого кода:
РеквизитФормыВЗначение("Объект") считывает текущую структуру ДанныеФормыСтруктура и создает в оперативной памяти сервера экземпляр типа ДокументОбъект. Все несохраненные изменения, которые пользователь внес на форме, попадают в этот новый объект.ДокументОбъект вызывают необходимые прикладные методы.ЗначениеВРеквизитФормы(ДокументОбъект, "Объект") перезаписывает свойства и табличные части формы обновленными данными из объекта. После этого форма переходит в состояние Модифицированность = Истина.Важный момент: Данный метод работает в контексте формы и доступен только в процедурах с директивой &НаСервере. В процедурах &НаСервереБезКонтекста метод РеквизитФормыВЗначение() недоступен.
Посмотрим на второй подход, когда экземпляр объекта создается путем вызова метода ПолучитьОбъект() от ссылки:
&НаСервере
Процедура ВыполнитьДействиеИзБазыНаСервере()
// Получаем объект из базы данных по ссылке
ДокументОбъект = Объект.Ссылка.ПолучитьОбъект();
// Выполняем действия с объектом
ДокументОбъект.ЗаполнитьПоОстаткам();
ДокументОбъект.Записать();
// Обновляем отображение данных на форме
ОбновитьОтображениеДанных();
КонецПроцедуры
Проанализируем ключевое отличие этого варианта от первого. Метод ПолучитьОбъект() обращается напрямую к базе данных. Это означает, что:
ДокументОбъект.ПолучитьОбъект() вернет неопределенное значение или вызовет ошибку.ОбновитьОтображениеДанных().Для наглядности зафиксируем отличия двух подходов в виде таблицы возможностей:
РеквизитФормыВЗначение() берет текущие несохраненные данные с экрана (из формы), а ПолучитьОбъект() считывает сохраненную версию из базы данных.РеквизитФормыВЗначение() успешно работает с новыми объектами, в то время как ПолучитьОбъект() требует наличия существующей ссылки в БД.РеквизитФормыВЗначение() и ЗначениеВРеквизитФормы() выполняет полную сериализацию и десериализацию данных формы. На объектах с большими табличными частями (тысячи строк) это может вызывать задержки.Чтобы избежать нагрузки на сервер при постоянных сериализациях формы, рассмотрим альтернативные архитектурные решения:
ДокументОбъект.&НаСервереБезКонтекста для явного преобразования переданного реквизита используйте методы ДанныеФормыВЗначение() и ЗначениеВДанныеФормы().ДокументОбъект.Записать(). Рекомендуется использовать штатные методы формы ЭтаФорма.Записать() или ЗаписатьНаСервере(), которые корректно обрабатывают блокировки, транзакции и обновление интерфейса.