Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Класс AssemblyLoadContext появился в .NET Core и недоступен в .NET Framework. Эта статья дополняет AssemblyLoadContext документацию по API с концептуальными сведениями.
Эта статья относится к разработчикам, реализующим динамическую загрузку, особенно разработчики платформы динамической загрузки.
Что такое AssemblyLoadContext?
Каждое приложение .NET 5+ и .NET Core неявно использует AssemblyLoadContext. Это поставщик среды выполнения для поиска и загрузки зависимостей. При загрузке зависимости AssemblyLoadContext, экземпляр вызывается, чтобы найти ее.
- AssemblyLoadContext предоставляет службу поиска, загрузки и кэширования управляемых сборок и других зависимостей.
- Для поддержки динамической загрузки и выгрузки кода создается изолированный контекст для загрузки кода и его зависимостей в собственном AssemblyLoadContext экземпляре.
Правила управления версиями
Один AssemblyLoadContext экземпляр ограничен загрузкой только одной версии Assembly для каждого простого имени сборки. При разрешении ссылки на сборку для экземпляра AssemblyLoadContext, в котором уже загружена сборка с таким именем, запрошенная версия сравнивается с загруженной версией. Разрешение будет выполнено успешно, только если загруженная версия равна или выше запрошенной версии.
Когда требуется несколько экземпляров AssemblyLoadContext?
Ограничение, что единственный AssemblyLoadContext экземпляр может загружать только одну версию сборки, может стать проблемой при динамическом подключении модулей кода. Каждый модуль компилируется независимо, и модули могут зависеть от разных версий модуля Assembly. Эта проблема часто возникает, когда разные модули зависят от разных версий часто используемой библиотеки.
Для поддержки динамической загрузки кода API предоставляет возможность загрузки конфликтующих версий одного и того же AssemblyLoadContext в одном и том же приложении. Каждый AssemblyLoadContext экземпляр предоставляет уникальный словарь, который сопоставляет каждый AssemblyName.Name экземпляр с конкретным Assembly экземпляром.
Он также предоставляет удобный механизм группировки зависимостей, связанных с модулем кода для последующей выгрузки.
Экземпляр по умолчанию AssemblyLoadContext.Default
Экземпляр AssemblyLoadContext.Default автоматически заполняется средой выполнения при запуске. Он использует обычную проверку для обнаружения всех статических зависимостей.
Он решает наиболее распространенные сценарии загрузки зависимостей.
Динамические зависимости
AssemblyLoadContext имеет различные события и виртуальные функции, которые можно переопределить.
Экземпляр AssemblyLoadContext.Default только поддерживает переопределение событий.
В статьях алгоритм загрузки управляемых сборок, алгоритм загрузки спутниковых сборок и алгоритм загрузки неуправляемой (машинной) библиотеки ссылаются на все доступные события и виртуальные функции. В статьях показано относительное положение каждого события и функции в алгоритмах загрузки. Эта статья не воспроизводит эту информацию.
В этом разделе рассматриваются общие принципы соответствующих событий и функций.
- Будьте повторяемыми. Запрос конкретной зависимости всегда должен привести к тому же ответу. Необходимо вернуть тот же загруженный экземпляр зависимостей. Это требование является фундаментальным для согласованности кэша. В частности, для управляемых сборок мы создадим Assembly кэш. Ключ кэша — это простое имя сборки. AssemblyName.Name
-
Как правило, не бросайте. Ожидается, что эти функции возвращают
null, вместо того чтобы вызывать, если не удается найти запрошенную зависимость. Бросок преждевременно завершит поиск и распространит исключение к вызывающему коду. Исключение должно быть ограничено непредвиденными ошибками, такими как поврежденная сборка или состояние вне памяти. - Избегайте рекурсии. Помните, что эти функции и обработчики реализуют правила загрузки для поиска зависимостей. Реализация не должна вызывать API-интерфейсы, которые активируют рекурсию. Код обычно вызывает функции загрузки AssemblyLoadContext , требующие определенного пути или ссылочного аргумента памяти.
-
Загрузите в корректный AssemblyLoadContext. Выбор места загрузки зависимостей зависит от приложения. Выбор реализуется этими событиями и функциями. Когда ваш код вызывает функции загрузки по пути в AssemblyLoadContext, вызывайте их на экземпляре, в котором вы хотите загрузить код. Иногда возвращение
null, позволяя AssemblyLoadContext.Default справляться с нагрузкой, может быть самым простым вариантом. - Помните о гонках потоков. Загрузка может активироваться несколькими потоками. AssemblyLoadContext обрабатывает гонки потоков путем атомарного добавления сборок в кэш. Экземпляр проигравшего гонки удаляется. В логике реализации не добавляйте дополнительную логику, которая не обрабатывает несколько потоков должным образом.
Как изолированы динамические зависимости?
Каждый AssemblyLoadContext экземпляр представляет уникальную область для Assembly экземпляров и Type определений.
Между этими зависимостями нет двоичной изоляции. Они изолированы просто потому, что не могут найти друг друга по имени.
В каждом из AssemblyLoadContextних:
- AssemblyName.Name может ссылаться на другой Assembly экземпляр.
-
Type.GetType может возвращать другой экземпляр типа для одного типа
name.
Общие зависимости
Зависимости можно легко совместно использовать между AssemblyLoadContext экземплярами. Общая модель заключается в том, что один AssemblyLoadContext загружает зависимость. Другой делится зависимостью, используя ссылку на загруженную сборку.
Совместное использование необходимо для сборок среды выполнения. Эти сборки можно загрузить только в AssemblyLoadContext.Default. То же самое необходимо для таких платформ, как ASP.NET, WPFили WinForms.
Рекомендуется загружать общие зависимости в AssemblyLoadContext.Default. Этот принцип общего доступа является распространённым шаблоном проектирования.
Общий доступ реализуется в коде пользовательского AssemblyLoadContext экземпляра. AssemblyLoadContext имеет различные события и виртуальные функции, которые можно переопределить. Когда любая из этих функций возвращает ссылку на Assembly экземпляр, загруженный в другой AssemblyLoadContext экземпляр, Assembly экземпляр используется совместно. Стандартный алгоритм загрузки передаёт загрузку на AssemblyLoadContext.Default, чтобы упростить общую схему обмена. Дополнительные сведения см. в разделе "Алгоритм загрузки управляемой сборки".
Проблемы преобразования типов
Если два AssemblyLoadContext экземпляра содержат определения типов с одинаковыми name, они не являются одинаковыми типами. Они одинаковы, если и только если они приходят из того же Assembly экземпляра.
Чтобы усложнить ситуацию, сообщения об исключениях, связанных с этими несоответствиями, могут быть недвусмысленными. Типы называются в сообщениях исключений по их простым именам типов. Обычное сообщение об исключении в данном случае представляет собой форму:
Объект типа "IsolatedType" не может быть преобразован в тип "IsolatedType".
Отладка проблем с преобразованием типов
Учитывая пару несовпадных типов, важно также знать:
- Каждый тип Type.Assembly.
- Каждый тип AssemblyLoadContext, который можно получить с помощью функции AssemblyLoadContext.GetLoadContext(Assembly).
Имея два объекта a, и b, будет полезно вычислить следующее в отладчике:
// In debugger look at each assembly's instance, Location, and FullName
a.GetType().Assembly
b.GetType().Assembly
// In debugger look at each AssemblyLoadContext's instance and name
System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(a.GetType().Assembly)
System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(b.GetType().Assembly)
Устранение проблем преобразования типов
Существует два шаблона проектирования для решения этих проблем преобразования типов.
Используйте общие типы. Этот общий тип может быть примитивным типом среды выполнения или может включать создание нового общего типа в общей сборке. Часто общий тип — это интерфейс , определенный в сборке приложения. Дополнительные сведения см. в статье о совместном использовании зависимостей.
Используйте методы маршаллинга для преобразования из одного типа в другой.