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