Как загрузить сборку во время выполнения перед событием AssemblyResolve?

На самом деле я попытался реализовать какие-то "статически связанные сборки" в моем решении. Поэтому я попробовал следующее:

  • Добавление ссылки на мою сборку с помощью CopyLocal = false
  • Добавление самого DLL файла в мое решение с помощью "Добавить как ссылку"
  • Добавление самого DLL файла в мои ресурсы с помощью "Добавить ресурс" - "Добавить существующий файл"
  • Добавление некоторого типа из моей сборки в Form1 как private MyObject temp = new MyObject();

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

AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
    {
        Assembly MyAssembly = AppDomain.CurrentDomain.Load(Properties.Resources.ExternalAssembly);
        return MyAssembly;
    };

Итак, это работает! Я могу загрузить свою сборку из файла ресурсов в AssemblyResolveEvent. Но это событие происходит только в том случае, если оно не может найти мою сборку нигде. Но как я могу загрузить мою сборку до.Net пытается искать в разных местах?

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

Я попробовал это в program.cs с помощью следующего метода Main()

static void Main()
{
    LoadMyAssemblies();
    AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => LoadMyAssemblies();
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

private static Assembly LoadMyAssemblies()
{
    Assembly result = AppDomain.CurrentDomain.Load(Properties.Resources.MyStaticAssembly);
    return result;
}

Но он все еще попадает в ResolveEventHandler. И гораздо лучше, если я снова загружу сборку и посмотрю на AppDomain.CurrentDomain.GetAssemblies(), я вижу, что моя сборка загружается дважды!

Итак, любая идея, почему моя загруженная сборка не будет учитываться при ее загрузке перед событием AssemblyResolve? С помощью отладчика я также вернул null, когда вызов пришел из AssemblyResolve, но в этом случае я получил исключение FileNotFoundException в начале.

Ответ 1

На всякий случай, когда вы не знали, есть инструмент под названием ILMerge из MS Research, который объединяет сборки в один файл.

Также вы можете создавать сборки с несколькими файлами, используя Инструмент компоновщика сборки.

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

Ответ 2

CLR Binder не знает, что LoadMyAssemblies() делает то же самое, что и событие AssemblyResolve, и что они оба пытаются найти одну и ту же сборку и загрузить ее.

Событие AssemblyResolve всегда запускается в тот момент, когда Binder решает, что он искал все возможные местоположения (которые доступны для поиска по этому приложению) и не смог найти совпадение.

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

Я продолжу и отвечу на вопрос о том, как избежать участия в событии AssemblyResolve 1) Поместите сборку в GAC. Что касается Binder, то GAC всегда выигрывает. 2) Поместите свою сборку на пробный путь и убедитесь, что Binder ее подбирает (посмотрите на статью "Как среда выполнения находит сборки" в MSDN для получения дополнительной информации об этом).