Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
De AssemblyLoadContext klasse is geïntroduceerd in .NET Core en is niet beschikbaar in .NET Framework. Dit artikel is een aanvulling op de API-documentatie AssemblyLoadContext met conceptuele informatie.
Dit artikel is relevant voor ontwikkelaars die dynamisch laden implementeren, met name frameworkontwikkelaars voor dynamisch laden.
Wat is AssemblyLoadContext?
Elke .NET 5+ en .NET Core-toepassing maakt impliciet gebruik van AssemblyLoadContext. Dit is de provider van de runtime voor het zoeken en laden van afhankelijkheden. Wanneer een afhankelijkheid wordt geladen, wordt een AssemblyLoadContext instantie aangeroepen om deze te vinden.
- AssemblyLoadContext biedt een service voor het zoeken, laden en opslaan van beheerde assembly's en andere afhankelijkheden.
- Om dynamisch laden en lossen van code te ondersteunen, wordt een geïsoleerde context gecreëerd voor het laden van code en zijn afhankelijkheden in hun eigen AssemblyLoadContext instantie.
Versiebeheerregels
Een afzonderlijke AssemblyLoadContext instantie is beperkt tot het laden van precies één versie van een Assembly per eenvoudige assemblynaam. Wanneer een assembly-verwijzing wordt opgelost tegenover een AssemblyLoadContext exemplaar dat al een assembly met die naam heeft geladen, wordt de aangevraagde versie vergeleken met de geladen versie. De oplossing slaagt alleen als de geladen versie gelijk is aan of hoger is dan de aangevraagde versie.
Wanneer hebt u meerdere AssemblyLoadContext-exemplaren nodig?
De beperking dat een enkele AssemblyLoadContext-instantie slechts één versie van een assembly kan laden, kan problematisch zijn bij het dynamisch laden van codemodules. Elke module wordt onafhankelijk gecompileerd en de modules kunnen afhankelijk zijn van verschillende versies van een Assembly. Dit is vaak een probleem wanneer verschillende modules afhankelijk zijn van verschillende versies van een veelgebruikte bibliotheek.
Ter ondersteuning van dynamisch laden van code biedt de AssemblyLoadContext API het laden van conflicterende versies van een Assembly in dezelfde toepassing. Elk AssemblyLoadContext exemplaar biedt een uniek woordenboek dat elke AssemblyName.Name aan een specifiek Assembly exemplaar koppelt.
Het biedt ook een handig mechanisme voor het groeperen van afhankelijkheden met betrekking tot een codemodule voor later verwijderen.
Het standaardvoorbeeld van AssemblyLoadContext
Het AssemblyLoadContext.Default exemplaar wordt automatisch ingevuld door de runtime bij het opstarten. Het gebruikt standaarddetectie om alle statische afhankelijkheden te lokaliseren en te vinden.
Hiermee worden de meest voorkomende scenario's voor het laden van afhankelijkheden opgelost.
Dynamische afhankelijkheden
AssemblyLoadContext heeft verschillende gebeurtenissen en virtuele functies die kunnen worden overschreven.
Het AssemblyLoadContext.Default exemplaar ondersteunt alleen het overschrijven van de gebeurtenissen.
De artikelen managed assembly loading algorithm, Satellite assembly loading algorithm, and Unmanaged (native) library loading algorithm verwijzen naar alle beschikbare gebeurtenissen en virtuele functies. In de artikelen worden de relatieve positie van elke gebeurtenis en functie in de laadalgoritmen weergegeven. In dit artikel worden die gegevens niet gereproduceerd.
In deze sectie worden de algemene principes voor de relevante gebeurtenissen en functies beschreven.
- Herhaalbaar zijn. Een query voor een specifieke afhankelijkheid moet altijd resulteren in hetzelfde antwoord. Dezelfde geladen afhankelijkheidsinstantie moet worden geretourneerd. Deze vereiste is essentieel voor cacheconsistentie. Voor beheerde assembly's maken we met name een Assembly cache. De cachesleutel is een eenvoudige assemblynaam, AssemblyName.Name.
-
Gooi meestal niet. Er wordt verwacht dat deze functies
nullretourneren, in plaats van een foutmelding geven wanneer de aangevraagde afhankelijkheid niet kan worden gevonden. Door te gooien wordt de zoekopdracht voortijdig beëindigd en wordt een uitzondering doorgegeven aan de beller. Het werpen of genereren moet worden beperkt tot onverwachte fouten, zoals een beschadigde assembly of een geheugen tekort. - Vermijd recursie. Houd er rekening mee dat deze functies en handlers de laadregels implementeren voor het vinden van afhankelijkheden. Uw implementatie mag geen API's aanroepen die recursie activeren. Uw code moet doorgaans AssemblyLoadContext-laadfuncties aanroepen waarvoor een specifiek pad of een verwijzingsargument voor geheugen is vereist.
-
Laad in de juiste AssemblyLoadContext. De keuze waar afhankelijkheden moeten worden geladen, is toepassingsspecifiek. De keuze wordt geïmplementeerd door deze gebeurtenissen en functies. Wanneer uw code AssemblyLoadContext load-by-path-functies aanroept, worden deze aangeroepen op het exemplaar waar u de code wilt laden. Soms is teruggeven aan
nullen laten AssemblyLoadContext.Default de last dragen misschien de eenvoudigste optie. - Houd rekening met threadraces. Laden kan worden geactiveerd door meerdere threads. AssemblyLoadContext behandelt threadraces door assemblies op atomische wijze toe te voegen aan de cache. Het exemplaar van de raceverliezer wordt verwijderd. Voeg in uw implementatielogica geen extra logica toe waarmee meerdere threads niet goed worden verwerkt.
Hoe worden dynamische afhankelijkheden geïsoleerd?
Elk AssemblyLoadContext exemplaar vertegenwoordigt een uniek bereik voor Assembly exemplaren en Type definities.
Er is geen binaire isolatie tussen deze afhankelijkheden. Ze zijn alleen geïsoleerd omdat ze elkaar niet op naam kunnen vinden.
In elk AssemblyLoadContext:
- AssemblyName.Name kan verwijzen naar een ander Assembly exemplaar.
-
Type.GetType kan een ander typeexemplaren retourneren voor hetzelfde type
name.
Gedeelde afhankelijkheden
Afhankelijkheden kunnen eenvoudig worden gedeeld tussen AssemblyLoadContext exemplaren. Het algemene model is dat één AssemblyLoadContext een afhankelijkheid laadt. De andere deelt de afhankelijkheid door gebruik te maken van een verwijzing naar de geladen assemblage.
Dit delen is vereist voor de runtime-assembly's. Deze assemblies kunnen alleen in de AssemblyLoadContext.Default. Hetzelfde is vereist voor frameworks zoals ASP.NET, WPFof WinForms.
Het wordt aanbevolen om gedeelde afhankelijkheden te laden in AssemblyLoadContext.Default. Dit delen is het algemene ontwerppatroon.
Delen wordt geïmplementeerd in de code van de aangepaste AssemblyLoadContext instantie. AssemblyLoadContext heeft verschillende gebeurtenissen en virtuele functies die kunnen worden overschreven. Wanneer een van deze functies een verwijzing retourneert naar een instantie die in een andere Assembly instantie is geladen, wordt de AssemblyLoadContext instantie gedeeld. Het standaardlaadalgoritme vertrouwt op AssemblyLoadContext.Default voor het laden om het algemene patroon voor delen te vereenvoudigen. Zie het algoritme voor het laden van beheerde assembly's voor meer informatie.
Typeconversieproblemen
Wanneer twee AssemblyLoadContext exemplaren typedefinities met hetzelfde namebevatten, zijn ze niet hetzelfde type. Ze zijn hetzelfde type als en alleen als ze afkomstig zijn van hetzelfde Assembly exemplaar.
Om het ingewikkeld te maken, kunnen uitzonderingsberichten over deze niet-overeenkomende typen verwarrend zijn. De typen worden in de uitzonderingsberichten aangeduid op basis van hun eenvoudige typenamen. Het algemene uitzonderingsbericht in dit geval is van de vorm:
Het object van het type IsolatedType kan niet worden geconverteerd naar het type IsolatedType.
Typeconversieproblemen debuggen
Gezien een paar niet-overeenkomende typen, is het belangrijk om ook te weten:
- Elk type heeft Type.Assembly.
- Elk type heeft een AssemblyLoadContext, dat kan worden verkregen via de AssemblyLoadContext.GetLoadContext(Assembly) functie.
Gezien twee objecten a en bis het evalueren van het volgende in het foutopsporingsprogramma nuttig:
// 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)
Oplossen van problemen met typeconversie
Er zijn twee ontwerppatronen voor het oplossen van deze typeconversieproblemen.
Algemene gedeelde typen gebruiken. Dit gedeelde type kan een primitief runtimetype zijn, maar kan ook betrekking hebben op het maken van een nieuw gedeeld type in een gedeelde assembly. Vaak is het gedeelde type een interface die is gedefinieerd in een toepassingsassembly. Lees voor meer informatie over hoe afhankelijkheden worden gedeeld.
Gebruik marshalling-technieken om van het ene type naar het andere om te zetten.