2009-10-29 1 views
5

Можно ли это сделать, посылая сборку в память? Если да, то как это сделать?Как загрузить сборку без использования Assembly.Load?

Причина:

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

+4

Зачем вам это нужно? Почему вы не можете использовать Assembly.Load? –

+0

Я не хочу блокировать загруженную DLL. Я хочу иметь возможность динамически загружать, изменять код, компилировать и перезагружать его снова. –

+1

Тогда измените свой вопрос и спросите об этом, и я отвечу. –

ответ

11

Это может быть сделано путем перегрузки Load с использованием массива байтов. Вы должны прочитать сборки байтов перед нагрузкой и не заблокирует файл:

byte[] readAllBytes = File.ReadAllBytes("path"); 
Assembly assembly = Assembly.Load(readAllBytes); 
+1

Хорошо для загрузки, не так хорошо для разгрузки/перезагрузки. –

+0

Хенк, как вы имеете в виду, что это не хорошо для разгрузки? Выполнение этого снова не заменит существующие загруженные классы? –

+3

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

18

Как я понимаю, вы хотите сделать следующее:

  1. загрузить сборку с диска в память, чтобы использовать данные в нем или код вызова в нем
  2. Уметь выгружать сборку позже
  3. Избегайте блокировки сборки на диске, чтобы вы могли ее модифицировать без выхода из приложения (или выгрузить сборка первая)

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

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

Вот Google query, который должен предоставить вам некоторые исходные статьи.

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

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

Например, если ваша сборка A.dll подключается к второй сборке B.dll, и вы используете массив байтов для загрузки A.dll в память перед вызовом Assembly.Load, вам нужно либо обрабатывать разрешение сборки вызовы в вашем домене приложений (вам может быть рассказано всякий раз, когда сборка должна быть загружена, и «помогите» в процессе загрузки), или вам нужно убедиться, что B.dll загружается сначала так же, как и загрузка A.dll, в противном случае загрузка A.dll из памяти автоматически загрузит B.dll с диска.


Дополнительная информация об использовании отдельных доменов приложений.

Когда вы создаете другой домен приложения, используя класс AppDomain в .NET, вы создаете отдельный отсек в памяти, где вы можете запускать код. Он действительно отделен от вашего основного домена приложения и имеет лишь небольшое отверстие через стену, которая отделяет их.

Через это отверстие вы можете передавать сообщения, такие как вызовы методов и данные.

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

После загрузки сборки вы создаете один или несколько объектов внутри этого другого домена приложения таким образом, чтобы первый домен приложения мог разговаривать с этими объектами. Эти объекты должны опускаться от MarshalByRefObject, который является классом, который позволяет совершить какую-то магию.

В основном, что происходит. Внутри этого другого домена приложения создается объект типа, загружаемого в этот домен приложения. Этот тип происходит от MarshalByRefObject. Запрос на создание этого объекта поступал из первого домена приложения, а внутри этого домена приложения был создан прокси-объект, который выглядит, но не является тем же самым объектом, который был создан в этом другом домене приложения. Прокси-сервер связывается с этим другим объектом через эту дыру.

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

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

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

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

Я упомянул, что вы можете загрузить как минимум две сборки. Причиной этого является то, что если тип, который вы хотите построить объект, тип, объявленный в той сборке, которую вы хотите загрузить, не опускается с MarshalByRefObject, тогда проблема прохождения типов через это отверстие снова появляется, и вы также загрузите сборку в свой основной домен. Типичный способ справиться с этим - это иметь какой-то класс менеджера плагинов, который спускается с MarshalByRefObject, и этот менеджер сидит в этом другом домене и разговаривает с этими другими типами. Это позволяет избежать проблемы прохождения типов через это отверстие.

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

+0

+1 Отдельно AppDomain - это путь. –

+0

+1 действительно единственный способ добиться этого. Выгрузка «динамического» AppDomain позволяет избежать типичной утечки памяти. –

+0

Спасибо, когда вы загружаете сборку в отдельный appdomain, она доступна моему приложению? Как помогают апдомены? (Я не знаю). Будет ли это блокировать DLL? Потому что копирование сборки все время кажется грязным хаком. –

3

Вы можете создать временную копию сборки и загрузить эту копию с помощью Assembly.Load. Поместите монитор файла на оригинал и выгрузите/перезагрузите временную копию обновленной сборки, когда монитор файла обнаружит изменение.

1

Если скомпилировать DLL, на лету из источников, вы не обязательно должны даже сделать копию, вместо процесс перекомпилирования должен быть следующим:

1) Уничтожьте домен приложения, в котором загружены сборки, эффективно разблокируя библиотеки DLL.

2) Скомпилируйте сценарии.

3) Восстановите домен приложения для плагинов и загрузите в него новые сборки.

+0

С недостатком, что есть промежуток в несколько секунд, по крайней мере, между 1) и 3), где ваше приложение недоступно. –