Загрузка, использование и выгрузка сборки во время выполнения - PullRequest
1 голос
/ 01 декабря 2011

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

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

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

У меня есть интерфейс для задач, который включает в себя «запустить» и «убить»Метод (подпрограммы) и событие 'задача-шаг', 'завершить' и 'убить'.«taskstep» возвращает объект для последующего кэширования, «complete» срабатывает, когда вся задача выполнена, и «kill» срабатывает, когда она готова к выгрузке.Также должен быть установлен тайм-аут на весь процесс, равный двум часам, и тайм-аут на 2 минуты для события, вызвавшего остановку, в случае, если оно застряло, и в этот момент я хотел бы иметь возможность его выгрузить, заставив прекратить выполнение любых потоков (что являетсячто «убить» следует делать в любом случае).Каждая сборка может содержать несколько задач, каждая из которых должна загружаться, чтобы быть выгружаемой.

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

Я пытался унаследовать от MarshalByRefObject, но я даже не знаю полного имени сборки, если сначала не загрузил его, который затем блокируется.файл.Я попытался загрузить из массива байтов сборки.Это означает, что файл не заблокирован, но его копия остается в текущем домене приложения.Это станет проблематичным в течение следующих месяцев / лет!

For Each key As String In assemblies.Keys

    Dim ad As AppDomain = AppDomainHelper.BuildChildAppDomain(AppDomain.CurrentDomain, key)

    AddHandler ad.AssemblyResolve, AddressOf AssemblyResolve

    _l.Add(ad)

    For Each value As String In assemblies(key)
        Dim item As IScanner = CType(ad.CreateInstanceAndUnwrap(key, value), IScanner)
        ListBox1.Items.Add(item)
    Next
Next

Private Function AssemblyResolve(sender As Object, args As ResolveEventArgs) As Assembly
    Return GetType(IScanner).Assembly
End Function

Ответы [ 2 ]

1 голос
/ 01 декабря 2011

Рассмотрите возможность использования Managed Extensibility Framework .

Это часть .Net 4.0, и вы можете прочитать краткий обзор здесь .

Загрузкаи выгрузка сборок может быть очень плохой идеей - все работает не так, как вы ожидаете (когда-нибудь пытаетесь отловить исключение, выброшенное в другом домене приложений? BAM, вы не можете!)

0 голосов
/ 02 декабря 2011

Похоже, что исключение выдается из нового (вторичного, если хотите) домена приложения.Это означает, что ResolveEventArgs должен быть сериализован, чтобы пересечь границу домена приложения.Вы должны обработать событие из вторичного домена приложения, чтобы вам не приходилось пересекать эту границу.

так что ... создайте класс, который обрабатывает разрешения .. что-то вроде ...

Public Class AssemblyResolver
  Inherits MarshalByRefObject

  Public Property SearchPath As String = String.Empty

  Public Function ResolveAssembly(sender as Object, e As ResolveEventArgs) As Assembly

    Dim tPath As String = Path.Combine(SearchPath, e.Name & ".dll")

    If File.Exists(tPath) Then
      Return Assembly.LoadFrom(tPath)
    End If

    Return Nothing

  End Function

End Class

Теперь используйте его из загрузчика плагинов ...

Dim tPluginDomain As AppDomain = AppDomainHelper.BuildChildAppDomain(AppDomain.CurrentDomain, key)
Dim tResolver As AssemblyResolver = tPluginDomain.CreateInstanceAndUnwrap(GetType(AssemblyResolver).Assembly.FullName, GetType(AssemblyResolver).FullName)
AddHandler tPluginDomain.AssemblyResolve, AddressOf tResolver.ResolveAssembly

Это должно заставить вас указать правильную дорогу.
Весь код был с моей головы со ссылкой на MSDN.Не знаю, будет ли он скомпилирован .. ожидать опечатки / отладки.

РЕДАКТИРОВАТЬ К сожалению ... забыл пометить преобразователь в домене плагина.Исправлено.

...