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

Введение

В .NET Framework реализованы механизмы для запуска группы приложений в одном процессе – домены приложений (AppDomains). – это изолированный логический контейнер, в котором выполняется какая-либо задача со своей коллекцией сборок. В каждый домен можно загрузить только одну версию сборки, а вот выгрузить сборки из домена нельзя, можно выгрузить только домен целиком.

В .NET можно использовать только один домен приложения, но есть области загрузки сборок (AssemblyLoadContext). По умолчанию, все сборки загружаются в область AssemblyLoadContext.Default, но можно создавать новые области, в которые, в свою очередь, можно загружать разные версии сборок, а сами эти области можно выгружать. Важно!

Одинаковые типы из разных областей несовместимы, и, даже если одну и ту же сборку загрузить в разные области, типы из этих областей будут несовместимы между собой.

Суть проблемы

Имеет большое значение то, куда была загружена сборка:
  • если сборка явно добавлена в основное приложение, то чаще всего она загружается в дефолтную область, и доступна для всех других сборок;
  • если сборка используется в другой сборке-плагине, то она подгружается динамически, и чаще всего загружается в ту же область, что и плагин. И тут появляются два варианта:
    • если плагин загружается в основную область – проблем не возникает;
    • если плагин загружается в отдельную область – начинаются проблемы, о которых мы подробно расскажем далее.

Первая проблема

Суть проблемы в том, что некоторые команды «теряют» область, в которой выполняются, и поэтому не могут получить типы из этой области, и соответственно, работают неправильно.

Например, команда TypeDescriptor.GetConverter(type) обращается к системной сборке, которая может быть загружена только в дефолтную область, и во время работы «забывает» про вызывающую область. Поэтому если type находится в вызывающей области, команда не может получить этот тип и не может взять его конвертер. Эти конвертеры описаны у нас в атрибутах некоторых классов (StiMargin, StiBorder и т.п.) и используются при сериализации.

По этой причине:
  • не работает загрузка отчетов из формата XML, выдавая ошибку «... TypeConverter cannot convert from System.String»;
  • не работает компиляция отчётов.

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

Разработчики .NET не считают это большой проблемой, предлагая использовать workarounds.

Варианты решения проблемы
Вариант 1, простой, но с ограничениями:

Загрузка отчета: можно использовать файл .mrt с разметкой JSON, его загрузка работает по другому принципу и поэтому проблемы не возникает.

Режим вычисления: не использовать компиляцию как режим вычисления выражений в отчете, то есть, следует установить режим вычисления как Интерпретация.

Мы также сделали доработку – при возникновении такой ошибки режим вычисления отчета будет установлен как Интерпретация.

Вариант 2:
Можно обернуть вызов наших классов специальной командой, которая форсирует использование области:
StiReport stiReport;
using (AssemblyLoadContext.EnterContextualReflection(Assembly.GetExecutingAssembly()))
{
    stiReport = new StiReport();
    stiReport.Load(templateStream);
    stiReport.Render(false);
}
Важно!

Результат работы команды TypeDescriptor.GetConverter(type) кэшируется где-то внутри системной библиотеки, поэтому если сначала этот метод вызовется где-то без обёртки, то повторный вызов с правильной оберткой вернет первый неправильный результат.

Вторая проблема

This issue is similar to the previous one. The command Type.GetType(name) sometimes fails to retrieve the required types.

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

Третья проблема

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

Варианты решения проблемы
Мы реализовали небольшую доработку, и теперь это работает и в случае плагинов.
Используя этот сайт, вы соглашаетесь на использование файлов Cookie для аналитики и персонализированного контента. Файлы Cookie хранят полезную информацию на вашем компьютере, чтобы помочь нам повысить эффективность и удобство использования. Для получения дополнительной информации, пожалуйста, прочтите Конфиденциальность и Использование Cookie.