Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
La AssemblyLoadContext classe a été introduite dans .NET Core et n’est pas disponible dans .NET Framework. Cet article complète la documentation de l’API AssemblyLoadContext avec des informations conceptuelles.
Cet article concerne les développeurs qui implémentent le chargement dynamique, en particulier les développeurs de framework de chargement dynamique.
Qu’est-ce que AssemblyLoadContext ?
Chaque application .NET 5+ et .NET Core utilise AssemblyLoadContextimplicitement . C'est le fournisseur du runtime qui permet de localiser et de charger les dépendances. Chaque fois qu’une dépendance est chargée, une AssemblyLoadContext instance est appelée pour la localiser.
- AssemblyLoadContext fournit un service de localisation, de chargement et de mise en cache d’assemblys managés et d’autres dépendances.
- Pour prendre en charge le chargement et le déchargement de code dynamique, il crée un contexte isolé pour le chargement du code et ses dépendances dans leur propre AssemblyLoadContext instance.
Règles de contrôle de version
Une instance AssemblyLoadContext est limitée au chargement d’une version d’un Assembly par nomd’assembly simple. Lorsqu’une référence d’assembly est résolue par rapport à une AssemblyLoadContext instance qui a déjà un assembly de ce nom chargé, la version demandée est comparée à la version chargée. La résolution réussit uniquement si la version chargée est égale ou supérieure à la version demandée.
Quand avez-vous besoin de plusieurs instances AssemblyLoadContext ?
La restriction qu’une seule AssemblyLoadContext instance peut charger une seule version d’un assembly peut devenir un problème lors du chargement dynamique des modules de code. Chaque module est compilé indépendamment et les modules peuvent dépendre de différentes versions d’un Assembly. Il s’agit souvent d’un problème quand différents modules dépendent de différentes versions d’une bibliothèque couramment utilisée.
Pour prendre en charge le chargement dynamique de code, l'API AssemblyLoadContext permet le chargement de versions conflictuelles d’un Assembly dans la même application. Chaque AssemblyLoadContext instance fournit un dictionnaire unique qui mappe chacun AssemblyName.Name à une instance spécifique Assembly .
Il fournit également un mécanisme pratique pour regrouper les dépendances liées à un module de code pour le déchargement ultérieur.
Instance AssemblyLoadContext.Default
L’instance AssemblyLoadContext.Default est automatiquement remplie par le runtime au démarrage. Il utilise la détection par défaut pour localiser et rechercher toutes les dépendances statiques.
Il résout les scénarios de chargement de dépendances les plus courants.
Dépendances dynamiques
AssemblyLoadContext a différents événements et fonctions virtuelles qui peuvent être substitués.
L’instance AssemblyLoadContext.Default prend uniquement en charge la substitution des événements.
Les articles Algorithme de chargement d’assembly managé, algorithme de chargement d’assembly satellite et algorithme de chargement de bibliothèque non managé (natif) font référence à tous les événements et fonctions virtuelles disponibles. Les articles montrent la position relative de chaque événement et fonction dans les algorithmes de chargement. Cet article ne reproduit pas ces informations.
Cette section aborde les principes généraux des événements et fonctions pertinents.
- Soyez reproductible. Une requête pour une dépendance spécifique doit toujours entraîner la même réponse. La même instance de dépendance chargée doit être retournée. Cette exigence est fondamentale pour la cohérence du cache. Pour les assemblages gérés en particulier, nous créons une Assembly mémoire cache. La clé de cache est un nom d’assembly simple. AssemblyName.Name
-
En règle générale, ne jetez pas. On s'attend à ce que ces fonctions retournent
nullplutôt que de générer une exception lorsqu'elles n'arrivent pas à trouver la dépendance demandée. La levée met fin prématurément à la recherche et propage une exception à l’appelant. La levée doit être limitée à des erreurs inattendues telles qu’un assembly endommagé ou une condition de mémoire insuffisante. - Évitez la récursivité. N’oubliez pas que ces fonctions et gestionnaires implémentent les règles de chargement pour identifier les dépendances. Votre implémentation ne doit pas appeler les API qui déclenchent la récursivité. Votre code doit généralement appeler des fonctions de chargement AssemblyLoadContext qui nécessitent un chemin d’accès ou un argument de référence de mémoire spécifique.
-
Charger dans le bon AssemblyLoadContext. Le choix de l’emplacement de chargement des dépendances est spécifique à l’application. Le choix est implémenté par ces événements et fonctions. Lorsque votre code appelle des fonctions de chargement par chemin d’accès AssemblyLoadContext , appelez-les sur l’instance où vous souhaitez charger le code. Peut-être que retourner
nullet laisser AssemblyLoadContext.Default gérer la charge est l'option la plus simple. - Tenez compte des concurrences de threads. Le chargement peut être déclenché par plusieurs threads. AssemblyLoadContext gère les conflits de threads en ajoutant de manière atomique des assemblies à son cache. L’instance du perdant de course est ignorée. Dans votre logique d’implémentation, n’ajoutez pas de logique supplémentaire qui ne gère pas correctement plusieurs threads.
Comment les dépendances dynamiques sont-elles isolées ?
Chaque AssemblyLoadContext instance représente une étendue unique pour Assembly les instances et Type les définitions.
Il n’existe aucune isolation binaire entre ces dépendances. Elles ne sont isolées que parce qu’elles ne se trouvent pas par nom.
Dans chaque AssemblyLoadContext :
- AssemblyName.Name peut faire référence à une autre Assembly instance.
-
Type.GetType peut retourner une instance de type différente pour le même type
name.
Dépendances partagées
Les dépendances peuvent facilement être partagées entre les AssemblyLoadContext instances. Le modèle général permet à AssemblyLoadContext de charger une dépendance. L’autre partage la dépendance à l’aide d’une référence à l’assembly chargé.
Ce partage est requis pour les assemblies d'exécution. Ces assemblages ne peuvent être chargés que dans le AssemblyLoadContext.Default. Il en est de même pour les frameworks tels que ASP.NET, WPFou WinForms.
Il est recommandé de charger les dépendances partagées dans AssemblyLoadContext.Default. Ce partage est le modèle de conception courant.
Le partage est implémenté dans le codage de l’instance personnalisée AssemblyLoadContext . AssemblyLoadContext a différents événements et fonctions virtuelles qui peuvent être substitués. Quand l’une de ces fonctions retourne une référence à une Assembly instance chargée dans une autre AssemblyLoadContext instance, l’instance Assembly est partagée. L’algorithme de charge standard défère à AssemblyLoadContext.Default pour le chargement afin de simplifier le modèle de partage commun. Pour en savoir plus, voir l’algorithme de chargement d’assemblage géré.
Problèmes de conversion de type
Lorsque deux AssemblyLoadContext instances contiennent des définitions de type identiques name, elles ne sont pas du même type. Ils sont du même type si et uniquement s’ils proviennent de la même Assembly instance.
Pour compliquer les questions, les messages d’exception relatifs à ces types incompatibles peuvent être déroutants. Les types sont référencés dans les messages d’exception par leurs noms de types simples. Le message d’exception courant dans ce cas est de la forme suivante :
L’objet de type « IsolatedType » ne peut pas être converti en type « IsolatedType ».
Problèmes de conversion de type de débogage
Étant donné une paire de types incompatibles, il est important de savoir également :
- Chaque type est Type.Assembly.
- Chaque type est AssemblyLoadContext, et peut être obtenu via la fonction AssemblyLoadContext.GetLoadContext(Assembly).
Compte tenu de deux objets a et b, l’évaluation des éléments suivants dans le débogueur sera utile :
// 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)
Résoudre les problèmes de conversion de type
Il existe deux modèles de conception pour résoudre ces problèmes de conversion de type.
Utilisez des types partagés courants. Ce type partagé peut être un type d’exécution primitif ou impliquer la création d’un nouveau type partagé dans un assembly partagé. Souvent, le type partagé est une interface définie dans un assembly d’application. Pour plus d’informations, découvrez comment les dépendances sont partagées.
Utilisez des techniques de marshaling pour convertir un type à un autre.