Зачем нужен обработчик AssemblyResolve для уже загруженной сборки?

У меня две сборки: App и AddOn. App ссылки AddOn, но CopyLocal установлен на false, так как AddOn будет динамически загружаться с помощью App.

Вот код в AddOn:

namespace AddOn
{
    public class AddOnClass
    {
        public static void DoAddOnStuff()
        {
            Console.WriteLine("AddOn is doing stuff.");
        }
    }
}

и вот код в App:

class Program
{
    static void Main(string[] args)
    {
        Assembly.LoadFrom(@"..\..\..\AddOn\bin\Debug\AddOn.dll");

        // Without this event handler, we get a FileNotFoundException.
        // AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
        // {
        //     return AppDomain.CurrentDomain.GetAssemblies()
        //                     .FirstOrDefault(a => a.FullName == e.Name);
        //};

        CallAddOn();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void CallAddOn()
    {
        AddOnClass.DoAddOnStuff();
    }
}

Я не понимаю, почему код не работает с обработчиком AssemblyResolve, указанным в Main(). При запуске в Visual Studio отладчик разбивается на CallAddOn() с помощью FileNotFoundException. Почему он жалуется? Сборка загружена, и она представляет собой ту же самую версию (то есть файл на диске), как то, на что ссылался App.

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

Ответ 1

Причина в том, что существует несколько контекстов загрузки сборок. Контекст, который загружается сборкой, влияет на то, как его можно использовать. Когда сборка загружается средой выполнения с использованием механизма проверки по умолчанию, она помещается в так называемый контекст нагрузки. Это контекст, используемый при загрузке сборки через Assembly.Load. Вы загрузили сборку, используя LoadFrom, которая использует свой собственный контекст. Исследование не проверяет контекст LoadFrom, и файл не находится в пути проверки, поэтому вам необходимо разрешить его для среды выполнения. Однако это не является симметричным. Если сборка загружена в контексте загрузки, LoadFrom будет сначала загружать ее из нее (при условии, что идентификатор один и тот же. Для неподписанных сборок путь является частью идентификатора.). Отмечу, что есть больше контекстов, включая ReflectionOnlyLoad и ReflectionOnlyLoadFrom. LoadFile загружает сборку без контекста, т.е. все зависимости должны быть загружены вручную.

Если вы хотите, чтобы сборки были разрешены в контексте загрузки, но если они существуют вне пути поиска по умолчанию приложения, вы можете сделать это и через конфигурацию. Используйте либо <codebase> элемент переадресации связывания сборки, либо атрибут privatePath <probing>.

Прочтите этот для получения дополнительной информации. Есть также некоторые сообщения в блоге Suzanne Cook от времени назад по загрузке сборки и контекстам (см. здесь, здесь, и здесь).