Delen via


Over System.Runtime.Loader.AssemblyLoadContext

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 exemplaar aangeroepen om deze te vinden.

  • AssemblyLoadContext biedt een service voor het zoeken, laden en opslaan van beheerde assembly's en andere afhankelijkheden.
  • Ter ondersteuning van dynamisch laden en lossen van code wordt er een geïsoleerde context gemaakt voor het laden van code en de bijbehorende afhankelijkheden in hun eigen AssemblyLoadContext exemplaar.

Versiebeheerregels

AssemblyLoadContext Eén exemplaar is beperkt tot het laden van precies één versie van een Assembly eenvoudige assemblynaam. Wanneer een assembly-verwijzing wordt omgezet voor een AssemblyLoadContext exemplaar dat al een assembly van 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 die één AssemblyLoadContext exemplaar kan laden, kan slechts één versie van een assembly een probleem worden 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 unieke woordenlijst die aan AssemblyName.Name een specifiek Assembly exemplaar wordt toegewezen.

Het biedt ook een handig mechanisme voor het groeperen van afhankelijkheden met betrekking tot een codemodule voor later verwijderen.

Het AssemblyLoadContext.Default-exemplaar

Het AssemblyLoadContext.Default exemplaar wordt automatisch ingevuld door de runtime bij het opstarten. Er wordt gebruikgemaakt van standaardprompts om alle statische afhankelijkheden te zoeken 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. Hetzelfde geladen afhankelijkheidsexemplaren moeten 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 retourneren null in plaats van te gooien 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 genereren moet worden beperkt tot onverwachte fouten, zoals een beschadigde assembly of een onvoldoende geheugenvoorwaarde.
  • 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 retourneert null en kan de AssemblyLoadContext.Default belasting de eenvoudigste optie zijn.
  • Houd rekening met threadraces. Laden kan worden geactiveerd door meerdere threads. AssemblyLoadContext verwerkt threadraces door atomisch assembly's toe te voegen aan de cache. Het exemplaar van de race-loser 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 door elkaar niet op naam te vinden.

In elk AssemblyLoadContext:

Gedeelde afhankelijkheden

Afhankelijkheden kunnen eenvoudig worden gedeeld tussen AssemblyLoadContext exemplaren. Het algemene model is bedoeld om AssemblyLoadContext een afhankelijkheid te laden. De andere deelt de afhankelijkheid met behulp van een verwijzing naar de geladen assembly.

Dit delen is vereist voor de runtime-assembly's. Deze assembly's kunnen alleen in de AssemblyLoadContext.Default. Hetzelfde is vereist voor frameworks zoals ASP.NET, WPFof WinForms.

Het wordt aanbevolen om gedeelde afhankelijkheden in te AssemblyLoadContext.Defaultladen. Dit delen is het algemene ontwerppatroon.

Delen wordt geïmplementeerd in de codering van het aangepaste AssemblyLoadContext exemplaar. AssemblyLoadContext heeft verschillende gebeurtenissen en virtuele functies die kunnen worden overschreven. Wanneer een van deze functies een verwijzing retourneert naar een exemplaar dat in een Assembly ander AssemblyLoadContext exemplaar is geladen, wordt het Assembly exemplaar gedeeld. Het standaardbelastingsalgoritmen wordt uitgesteld tot AssemblyLoadContext.Default laden om het algemene patroon voor delen te vereenvoudigen. Zie het algoritme voor het laden van beheerde assembly's voor meer informatie.

Problemen met typeconversie

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 het formulier:

Het object van het type IsolatedType kan niet worden geconverteerd naar het type IsolatedType.

Problemen met typeconversie opsporen

Gezien een paar niet-overeenkomende typen, is het belangrijk om ook te weten:

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)

Problemen met typeconversie oplossen

Er zijn twee ontwerppatronen voor het oplossen van deze typeconversieproblemen.

  1. 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.

  2. Gebruik marshalltechnieken om van het ene type naar het andere te converteren.