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