Как адаптировать код для веб-клиента Directum RX

13 14

Предисловие

Десктоп-клиент всё ещё популярен у пользователей Directum RX. Один из аргументов в его пользу - это неработающая заказная разработка.

Но подождите, ведь заказную разработку мы делаем сами, почему она не работает в вебе? Примерно об этом мы сейчас и поговорим.

В общих чертах

Прежде всего имеет смысл оговорить, что всё нижесказанное действительно в первую очередь для клиентского кода, поскольку именно его выполнение отличается в вебе и десктопе. Отличие в том, что в вебе весь клиентский код выполняется на веб-сервере, а в декстопе – тут же на клиенте.
Все прикладные функции стандартного базового решения Directum RX написаны с учётом этого факта, и все платформенные методы тоже работают с его учётом. Однако сторонние библиотеки, подключаемые в Directum RX Development Studio (далее DDS), чаще всего об этой разнице не знают, поскольку она им не нужна. Поэтому к выбору сторонних библиотек также следует подходить с оглядкой.
И немного о работе с запрещёнными классами .NET. Запрещено всё, что не разрешено, а список разрешённых классов можно найти в справке. Игнорирование этих запретов всегда на совести разработчика, но если вдруг вам потребовалось использовать что-то из запрещённых классов, то и здесь лучше заранее подумать, как это будет работать в вебе.
Теперь, когда у нас есть некоторые вводные, перейдём к разбору конкретных случаев.

Выгрузка документов

В нашей практике эта функциональность в подавляющем большинстве случаев работала только в десктоп-клиенте. Например, у одного заказчика вплоть до версии 3.4 для выгрузки документов использовался алгоритм следующего вида:

  1. Запросить у пользователя на клиенте список документов или параметры для поиска.
  2. Запросить у пользователя на клиенте адрес папки для выгрузки.
  3. Где-то на сервере осуществить поиск документов по переданным параметрам и/или их обработку, например, преобразование в PDF, а результат передать на клиент.
  4. На клиенте сохранить обработанные документы в указанную папку.

Запрещённые конструкции использовались на шаге 4, в частности, классы из пространства имён System.IO. На шаге 2 подключалась внешняя библиотека, которая внутри также использовала запрещённые конструкции. Использовать такие обёртки разрешено, но как уже упоминалось выше, делать это нужно с умом. На версии 3.4 заказчик захотел перевести всех своих пользователей с десктоп-клиента на веб, но столкнулся с проблемой – выгрузка документов в вебе, что называется, не завелась. Код падал с ошибкой, и вот почему:

  1. Веб-сервер не может обработать вызов диалоговых окон из System.IO и передать их в веб-клиент для отображения пользователю.
  2. Веб-сервер просто не имеет доступа к клиентскому компьютеру. Даже если запросить у пользователя путь к папке для выгрузки в виде строки, файлы в лучшем случае сохранятся там же на веб-сервере, а в худшем код снова упадёт с ошибкой.

Исправить ситуацию решили заменой алгоритма выгрузки на такой:

  1. У пользователя на клиенте так же, как раньше, запрашивается список документов или параметры для поиска.
  2. На сервере выполняется поиск и обработка документов, после чего результаты упаковываются в ZIP-архив, который передаётся на клиент. 
  3. На клиенте выполняется экспорт ZIP-архива. Загрузка и сохранение файла будут произведены средствами используемого браузера.

У этой реализации есть один нюанс: способ с экспортом ZIP-архива работает только в веб-клиенте. Старую схему с использованием System.IO можно оставить для совместимости (например, сделать в коде ветвление в зависимости от типа клиента), а можно убрать, в зависимости от того, требуется ли форсировать переход пользователей из десктоп-клиента в веб. 
Также есть альтернативный вариант выгрузки документа не в локальную папку на компьютере пользователя, а в общую папку на сервере. Такую выгрузку можно реализовать в асинхронном обработчике, который по завершении своей работы пришлёт пользователю уведомление об успешной выгрузке со ссылкой на выгруженный файл.

Отправка документов по электронной почте

Эта функциональность, напротив, была самой экзотической для адаптации, но в то же время самой простой. Возможность вложить в письмо приложения к документу появилась в Directum RX сравнительно недавно, в версии 3.3. До этого разработчики выкручивались примерно так:

      var docs = new List<Sungero.Content.IElectronicDocument>();
      foreach (var doc in documents)
      {
        docs.Add(doc);
        if (doc.HasRelations)
          docs.AddRange(doc.Relations.GetRelated(AppModule.Constants.Module.AddendumRelationName));
      }
      
      var versions = docs.Where(d => d.HasVersions).Distinct().Select(d => d.LastVersion);
      
      var content = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.Contains("Sungero.Content.Client,")).SingleOrDefault();
      var helper = content.GetTypes().Single(t => t.Name == "ElectronicDocumentHelper");
      var method = helper.GetMethod("SendByMail");
      if (method != null)
        method.Invoke(null, new object[] {versions});


И это прекрасно работало, если не учитывать, что:

  1. Использование рефлексии тоже запрещено.
  2. Имена классов/типов и т.д. могут изменяться от версии к версии (отчасти именно из-за этого существует п.1).
  3. Приведённый кусок кода сработает только в десктоп-клиенте.

После обновления на версию 3.3 заказчик решил использовать веб-клиент вместо десктопа, где и выяснилось, что приведённый кусок кода превратился в тыкву. Хорошая новость была в том, что теперь его можно было с чистой совестью удалить, потому что аналогичная функциональность появилась в стандартной разработке Directum RX.

Здесь могла быть ваша реклама история

За всё время, что существует веб-клиент Directum RX, у нас было достаточно случаев, когда пользователи не могли работать в веб-клиенте, чтобы увидеть в этом некую тенденцию. Тем удивительнее для нас оказался результат - все эти случаи удалось описать всего в двух историях выше. Мы уверены, что у вас тоже найдётся похожий кейс, а может быть, и не один. Делитесь ими в комментариях - возможно, именно ваше решение окажется полезным другим пользователям сообщества :)

Дмитрий Панкрашов

Добрый день, Александра, спасибо за статью. По поводу разрешенных и запрещенных конструкций не хватает описаний, и, как следствие, нет понимания, что и в какой момент может выстрелить. Например, согласно справке, запрещено использовать new, кроме случаев, когда создается коллекция. Однако, в базовой разработке есть обратные примеры. Та же история с перехватом исключений.

try {
 //some code
}
catch (Exception ex) {
  Logger.Error(ex.Message);
}

Вот такой код наблюдается в тех же самых базовых решениях, и, опять же вопреки справке, исключение не кидается дальше по стеку. Хотелось бы увидеть статью на эту тему.

Артем Моисеев

Смотрю справку и вижу запрещенные возможности System.IO, мне System.IO подключать как стороннюю библиотеку?

Пример 1. Сохранить содержимое файла в новую версию документа

var dfile = Dialogs.CreateInputDialog("Выберите файл");

var file = dfile.AddFileSelect("Файл", false);

// Получить содержимое файла.

var fileContent = file.Value.Content;

// Сохранить содержимое файла в новую версию документа.

using (var memory = new System.IO.MemoryStream(fileContent))

{

 var document = Sungero.Content.ElectronicDocuments.Create();

 document.CreateVersion();

 var version = document.LastVersion;

 version.AssociatedApplication = Sungero.Content.AssociatedApplications.GetByExtension("xml");

 version.Body.Write(memory);

 document.Save();

}

Пример 2. Получить полный путь до файла

using System.IO;

var dfile = Dialogs.CreateInputDialog("Выберите файл");

var file = dfile.AddFileSelect("Файл", false);

// Получить имя файла.

var fileName = file.Value.Name;

// Получить полный путь до файла.

string tempPath = System.IO.Path.GetTempPath();

string filePath = string.Format(@"{0}{1}",tempPath, fileName);

Артем Моисеев

вообще с переходом на веб надо менять и среду разработки, ну что за контруктор формы, делаю одно, ьа а вебе другое, нужен конструктор форм не из десктопа, а из веба, плюс разделение на серверные и клиентские функции можно убирать, раз получение сущностей работает в клиентском коде в вебе, так как клиентские код выполняется на сервере. Будет новый DDS?

Дмитрий Панкрашов

Артем, System.IO надо просто прописать в верхнем блоке с директивами Using, но, раз уж эти возможности такие "запрещенные" то лучше их вынести в отдельную библиотеку

Валерий Позднев

Ну, а ничего, что поведение пулов у тонкого и толстого может отличаться? Из вчерашнего: после установки доработки тонкий сыплет эксепшенами в лог и пользаков не пускает. Лог толстого чист и логины идут. Понятно, что надо исправлять доработку, но сам факт: "не работает в вебе.. а давай-как я поработаю в десктопе.. о! все работает!". Ну или вот выгрузка от сервера отчета - веб кричит, что сервер недоступен. Толстяк покряхтел, и принял отчет. Вот такие вот моменты  вкупе с вышеперечисленными и создают вебу репутацию "младшего брата". Не так чтобы слабоумного, но, все же, неполноценного.

Дмитрий Панкрашов

Артем, интересная ситуация.

Вот тут (https://club.directum.ru/webhelp/directumrx/sds/index.html?sds_zapreshchennye_classy.htm#anchor_prochie_classy_net_framework) четко написано, что: 

Запрещается использовать все классы .NET Framework, кроме разрешенных. Например, запрещается работать c:

файлами, папками, путями;

 

Однако в справке (https://club.directum.ru/webhelp/directumrx/sds/index.html?om_vybor_faila_addfileselect.htm) есть примеры кода, которые изобилуют запрещенными возможностями:

  • using (var memory = new System.IO.MemoryStream(fileContent)) (тут сразу 2 запрета нарушено)
  • string tempPath = System.IO.Path.GetTempPath();

Непонятно, можно ли использовать примеры из справки, если они содержат запрещенный код.

Иван Романов

Артем, если будет использоваться та же DCS, то разделение важно, т.к. DCS работает как десктоп-клиент.

Иван Романов

По System.IO - как ни крути, он используется. Да, нельзя. Но используется, причем довольно таки успешно.

Павел Леонов

Иван, Никто не мешает организовать свой отправщик, используя web-api, либо что-то другое. DCS - отдельный продукт и решение о механизмах интеграции принимается командой внедрения.

Сергей Беляков

Добрый день.

По поводу кейса "Отправка документов по электронной почте", а почему не использовали System.Net.Mail ?

Решение конечно интересное, но как мне кажется излишне переусложненное.

 

Сергей Беляков

Дмитрий, Да по сути можно использовать все что работает, от System.IO, до параллельных потоков и Environment, если правильно этим пользоваться.

1) System.IO бесполезен в веб, т.к. на клиенте отображается только результат серверных вычислений, кстати по этой причине Create() любого объекта в клиентском слое будет работать в веб клиенте (но так все равно делать не надо).

2) Как уже писали в статье, в вебе документ не из системы (результат отчета к примеру) можно выгрузить только через IZip, у которого есть метод Export(). Ну или заносить его как SimpleDocument и делать экспорт из него, но это лютый костыль. В десктопе можно массив байт записать сразу на диск или в папку и никаких проблем.

3) В диалогах Desktop клиента можно работать с папками на клиенте, чего не позволяет сделать Web.

4) Рефлексия типа запрещена, но есть моменты когда без нее не обойтись. 

Ну и т.д. и т.п.

Если все собрать, то наверное на целую статью хватит.

Александра Бабушкина

Сергей, ответа на этот вопрос у меня нет. Нам код достался уже в таком виде, а когда дошло до обновления и решения проблемы, никто из авторов уже не мог вспомнить, почему так написали. Могу лишь предположить, что использовать уже готовый стандартный метод, даже вытащенный таким способом, оказалось быстрее и проще, чем писать собственную логику создания и отправки письма через System.Net.Mail.

Александра Бабушкина

Сергей, спасибо за поддержку и развёрнутый ответ :) ключевое здесь - "если правильно этим пользоваться", а с оценкой "правильно или неправильно мы здесь это применяем" на первых порах могут быть проблемы.

Александра Бабушкина

Дмитрий, есть ощущение, что одной статьи здесь не хватит  но мы подумаем в эту сторону

Авторизуйтесь, чтобы написать комментарий