В этой статье:

Описание проблемы

Сериализация в XML во внешних сборках

Доработка файла проекта

Проблемы при работе с внешними сборками

Описание проблемы

В «Форсайт. Аналитическая платформа» существует возможность загрузки и использования внешних сборок. Выполняемые сборки загружаются в домен приложения и остаются там до момента завершения всего приложения. Если в методах внешней сборки осуществляется динамическая загрузка других сборок, то эти сборки также попадают в домен текущего приложения и остаются там до его завершения. Из-за этого может возникнуть ситуация, описанная ниже.

Примечание. В .NET Framework для динамической загрузки сборок используются различные методы Load* класса System.Reflection.Assembly. Также динамическая загрузка производится при работе некоторых системных сборок, в частности при сериализации с помощью System.Xml.Serialization.XmlSerializer.

Допустим, разработанная внешняя сборка загружена в .NET-сборку репозитория, обозначим ее как сборка A. После каких-либо доработок внешняя сборка была загружена в другую .NET-сборку репозитория - сборку B. При этом наименования всех типов, реализованных во внешней сборке, и наименование самой сборки остались неизменными. Какой-либо метод внешней сборки осуществляет динамическую загрузку и работу с различными типами другой внешней сборки - сборки Test. При использовании сборок А и B в репозитории произойдет следующее:

Данная проблема решаема с помощью своевременной выгрузки сборок из домена приложения, но ввиду особенностей реализации работы со сборками в «Форсайт. Аналитическая платформа», проблема остается открытой. Её решение запланировано на более поздние версии «Форсайт. Аналитическая платформа».

Если динамическая загрузка осуществляется в коде внешней сборки, то рекомендуется отказаться от нее и использовать ссылки на сборку в настройках проекта.

Если во внешней сборке используется сериализатор в XML (XmlSerializer), то используйте путь обхода, описанный ниже.

Сериализация в XML во внешних сборках

Для решение проблемы с сериализатором, который во время своей работы динамически загружает в память сборку с сериализуемыми типами, существует следующей путь обхода: необходимо объединить исходную сборку со сборкой сериализации XML, предварительно изменив код для инициализации сериализатора (измененный код смотрите в примере ниже). Это позволит использовать сериализатор, хранящийся в теле сборки, и соответственно инициализирующийся для того экземпляра сборки, с которым будет производиться работа.

Сборка сериализации XML содержит оболочку для инструмента создания XML-сериализатора. Для создания сборки сериализации используется утилита SGen, поставляемая вместе с Microsoft Visual Studio. Команда для создания сборки сериализации: SGen <ИмяСборки>.DLL. После выполнения данной команды в папке, где находится библиотека сборки, будет создана еще одна библиотека - <ИмяСборки>.XmlSerializers.dll. Для объединения двух сборок используйте программу ILMerge (Программа доступна для скачивания на сайте http://www.microsoft.com/). Команда для объединения сборок: ILMerge /t:library /out: <КонечнаяСборка>.dll <ИмяСборки>.dll <ИмяСборки>.XmlSerializers.dll. Полученная объединенная сборка, а также различные ее модификации, могут быть загружены в разные .NET-сборки репозитория и использованы параллельно.

Рассмотрим процесс объединения сборок на следующем примере:

Имеется сборка, разрабатываемая на языке C# в среде Microsoft Visual Studio. Тело сборки включает в себя следующий код:

using System;

using System.Xml;

using System.Xml.Serialization;

namespace TestAssembly

{

    [Serializable()]

    public class Test

    {

        //...

        //Сериализация объекта в XML

        public void ToXML(string path)

        {

            //...

            XmlSerializer s = new XmlSerializer(this.GetType());

            //...

        }

        //...

        // Десериализация объекта из XML

        public Test FromXml(string path)

        {

            //...

            XmlSerializer s = new XmlSerializer(this.GetType());

            //...

        }

        //...

    }

}

Процедура ToXML используется для сериализации в XML текущего экземпляра объекта класса Test. Функция FromXml осуществляет десериализацию объекта из файла, результатом ее работы является объект класса Test. Для возможности использования указанной сборки и нескольких ее модификаций в «Форсайт. Аналитическая платформа» изменим код для инициализации сериалайзера следующим образом:

XmlSerializer s = (XmlSerializer)Assembly.GetExecutingAssembly().CreateInstance("Microsoft.Xml.Serialization.GeneratedAssembly.TestSerializer");

На данном этапе тип "Microsoft.Xml.Serialization.GeneratedAssembly.TestSerializer" еще не существует, он будет доступен после получения сборки сериализации. Скомпилируйте сборку. Для генерирования сборки сериализации перейдите в папку проекта \bin\Debug\ и выполните в командной строке следующую команду:

SGen TestAssembly.dll

Примечание. Путь к утилите SGen должен быть добавлен в системную переменную Path в операционной системе.

При выполнении команды в этой же папке будет создана библиотека TestAssembly.XmlSerializers.dll. Если просмотреть содержимое этой библиотеки, то мы увидим в её составе пространство имен Microsoft.Xml.Serialization.GeneratedAssembly, в котором имеется класс TestSerializer. Именно этот класс и будет использоваться для инициализации сериалайзера.

Для объединения сборок установите программу ILMerge и выполните следующую команду:

ILMerge /t:library /out:TestXAssembly.dll TestAssembly.dll TestAssembly.XmlSerializers.dll

После выполнения команды будет создана объединенная сборка TestXAssembly.dll. Именно эту сборку можно загрузить в разные .NET-сборки репозитория.

Доработка файла проекта

Указанные команды для генерации сборки сериализации и объединения сборок могут быть заменены внесением доработок в файл проекта разрабатываемой сборки. В конце файла проекта предусмотрено место для формирования двух разделов <Target Name="BeforeBuild"> и <Target Name="AfterBuild">. В нашем случае создадим раздел <Target Name="AfterBuild"> и укажем в нем следующий код:

  <Target Name="AfterBuild" DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource" Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)" Outputs="$(OutputPath)$(_SGenDllName)">

    <!-- Удаление файла сериализации, если он уже был создан -->

    <Delete Files="$(TargetDir)$(TargetName).XmlSerializers.dll" ContinueOnError="true" />

    <!-- Создание нового файла сериализации -->

    <SGen BuildAssemblyName="$(TargetFileName)" BuildAssemblyPath="$(OutputPath)" References="@(ReferencePath)" ShouldGenerateSerializer="true" UseProxyTypes="false" KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)" DelaySign="$(DelaySign)" ToolPath="$(TargetFrameworkSDKToolsDirectory)" Platform="$(Platform)">

      <Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />

    </SGen>

    <!-- Создание временной папки, куда будет помещена объединенная сборка -->

    <MakeDir Directories="$(TargetDir)merged"/>

    <Exec Command="ILMerge /t:library /out:$(TargetDir)merged\$(TargetFileName) $(TargetDir)$(TargetFileName) $(TargetDir)$(TargetName).XmlSerializers.dll"/>

    <!-- Удаление всех файлов, созданных при компиляции -->

    <Exec Command="del $(TargetDir)$(TargetName)*.* /q"/>

    <!-- Перемещение объединенной сборки в основную папку проекта, где хранятся результаты компиляции -->

    <Exec Command="move $(TargetDir)merged\*.* $(TargetDir)"/>

    <!-- Удаление временной папки -->

    <RemoveDir Directories="$(TargetDir)merged"/>

  </Target>

Данные изменения позволяют произвести все действия со сборками во время компиляции текущего проекта.

См. также:

База знаний разработчиков