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