Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az AssemblyLoadContext osztály a .NET Core-ban lett bevezetve, és nem érhető el a .NET-keretrendszerben. Ez a cikk elméleti információkkal egészíti ki az AssemblyLoadContext API-dokumentációt.
Ez a cikk a dinamikus betöltést megvalósító fejlesztők számára fontos, különösen a dinamikus betöltési keretrendszer fejlesztői számára.
Mi az AssemblyLoadContext?
Minden .NET 5+ és .NET Core-alkalmazás implicit módon használja AssemblyLoadContext. Ez a futtatókörnyezet szolgáltatója a függőségek lokalizálásához és betöltéséhez. Amikor betölt egy függőséget, a rendszer meghív egy AssemblyLoadContext példányt, hogy megkeresse azt.
- A AssemblyLoadContext szolgáltatás a felügyelt szerelvények és egyéb függőségek azonosítását, betöltését és gyorsítótárazását biztosítja.
- A dinamikus kódbetöltés és -eltávolítás támogatása érdekében a kód és függőségei saját AssemblyLoadContext példányban való betöltéséhez elkülönített környezetet hoz létre.
Verziószámozási szabályok
Egyetlen AssemblyLoadContext példány korlátozott, hogy pontosan egy Assembly verziót töltsön be egy egyszerű szerelvénynév alapján. Ha a rendszer felold egy szerelvényhivatkozást egy AssemblyLoadContext olyan példányon, amely már rendelkezik ilyen nevű szerelvénysel, a rendszer összehasonlítja a kért verziót a betöltött verzióval. A megoldás csak akkor lesz sikeres, ha a betöltött verzió egyenlő vagy magasabb a kért verzióval.
Mikor van szüksége több AssemblyLoadContext-példányra?
A kódmodulok dinamikus betöltésekor problémát okozhat az a korlátozás, hogy egy AssemblyLoadContext példány csak egy szerelvény egyetlen verzióját töltheti be. Az egyes modulok függetlenül kerülnek lefordításra, és a modulok eltérő verzióktól Assembly függhetnek. Ez gyakran problémát jelent, ha a különböző modulok a gyakran használt kódtár különböző verzióitól függnek.
A kód dinamikus betöltésének támogatása érdekében az AssemblyLoadContext API lehetővé teszi egy Assembly adott alkalmazás ütköző verzióinak betöltését. Minden AssemblyLoadContext példány egyedi szótárat biztosít, amely mindegyiket AssemblyName.Name egy adott Assembly példányhoz rendeli.
Emellett kényelmes mechanizmust biztosít a kódmodulhoz kapcsolódó függőségek csoportosítására a későbbi eltávolításhoz.
Az AssemblyLoadContext.Default példánya
A AssemblyLoadContext.Default példányt a futtatókörnyezet automatikusan feltölti indításkor. Az alapértelmezett keresés használatával megkeresi az összes statikus függőséget.
A leggyakoribb függőségbetöltési forgatókönyveket oldja meg.
Dinamikus függőségek
AssemblyLoadContext különböző eseményekkel és virtuális függvényekkel rendelkezik, amelyeket felül lehet bírálni.
A AssemblyLoadContext.Default példány csak az események felülírását támogatja.
A felügyelt szerelvénybetöltési algoritmus, a műholdas szerelvénybetöltési algoritmus és a nem felügyelt (natív) kódtár betöltési algoritmusa az összes rendelkezésre álló eseményre és virtuális függvényre vonatkozik. A cikkek az egyes események és függvények relatív pozícióját mutatják be a betöltési algoritmusokban. Ez a cikk nem reprodukálja ezeket az információkat.
Ez a szakasz a releváns események és funkciók általános alapelveit ismerteti.
- Legyen ismételhető. Egy adott függőség lekérdezésének mindig ugyanazt a választ kell eredményeznie. Ugyanazt a betöltött függőségi példányt kell visszaadni. Ez a követelmény alapvető fontosságú a gyorsítótár-konzisztencia szempontjából. Különösen a felügyelt szerelvényekhez hozunk létre egy Assembly gyorsítótárat. A gyorsítótárkulcs egy egyszerű szerelvénynév, AssemblyName.Name.
-
Általában nem dob. Elvárható, hogy ezek a függvények
null-t adnak vissza, ahelyett hogy hibát dobnának, amikor nem találják a kért függőséget. A dobás idő előtt véget vet a keresésnek, és egy kivételt propagál a hívónak. A dobást olyan váratlan hibákra kell korlátozni, mint a sérült összeállítás vagy a memóriakimerülés. - Rekurzió elkerülése Vegye figyelembe, hogy ezek a függvények és kezelők implementálják a függőségek helyének meghatározására vonatkozó betöltési szabályokat. Az implementáció nem hívhatja meg a rekurziót kiváltó API-kat. A kódnak általában olyan AssemblyLoadContext terhelési függvényeket kell meghívnia, amelyek egy adott elérési utat vagy memóriahivatkozási argumentumot igényelnek.
-
Betöltés a megfelelő AssemblyLoadContextbe. A függőségek betöltésének helye alkalmazásspecifikus. A választást ezek az események és függvények valósítják meg. Amikor a kód meghívja a AssemblyLoadContext load-by-path függvényeket, hívja meg őket azon a példányon, ahol be szeretné tölteni a kódot. Előfordulhat, hogy a legegyszerűbb megoldás az, ha a
nullkezeli a terhelést, és a AssemblyLoadContext.Default intézi azt. - Legyen tisztában a szálversenyekkel. A betöltést több szál is aktiválhatja. Az AssemblyLoadContext úgy kezeli a szálversenyeket, hogy atomi módon hozzáadja az assembly-ket a gyorsítótárához. A verseny vesztesének példánya el lesz távolítva. A megvalósítási logikában ne adjon hozzá olyan extra logikát, amely nem kezeli megfelelően a több szálat.
Hogyan vannak elkülönítve a dinamikus függőségek?
Minden AssemblyLoadContext példány egyedi hatókört jelöl a Assembly példányok és a Type definíciók számára.
Ezek között a függőségek között nincs bináris elkülönítés. Úgy vannak elkülönítve, hogy nem találják egymást a nevük alapján.
Mindegyikben AssemblyLoadContext:
- AssemblyName.Name hivatkozhat egy másik Assembly példányra.
-
Type.GetType egy másik típuspéldányt adhat vissza ugyanahhoz a típushoz
name.
Megosztott függőségek
A AssemblyLoadContext példányok között a függőségek könnyen megoszthatók. Az általános modell egy AssemblyLoadContext függőség betöltésére használható. A másik a betöltött assembly-re mutató hivatkozással osztja meg a függőséget.
A futtatókörnyezeti kódgyűjtemények esetében szükséges ez a megosztás. Ezeket az összeállításokat csak a AssemblyLoadContext.Default-be lehet betölteni. Ugyanez szükséges az olyan keretrendszerekhez is, mint az ASP.NET, WPFvagy WinForms.
Javasoljuk, hogy a megosztott függőségeket betöltse a AssemblyLoadContext.Defaultrendszerbe. Ez a megosztás a közös tervezési minta.
A megosztás az egyéni AssemblyLoadContext példány kódolásában valósul meg. AssemblyLoadContext különböző eseményekkel és virtuális függvényekkel rendelkezik, amelyeket felül lehet bírálni. Ha ezen függvények bármelyike visszaad egy hivatkozást egy Assembly példányra, amelyet egy másik AssemblyLoadContext példányban töltöttek be, akkor a Assembly példány megosztásra kerül. A szabványos betöltési algoritmus a AssemblyLoadContext.Default-ra támaszkodik a betöltéshez, hogy leegyszerűsítse a gyakori megosztási mintát. További információ: Felügyelt szerelvénybetöltési algoritmus.
Típuskonvertálási problémák
Ha két AssemblyLoadContext példány azonos nametípusdefiníciókat tartalmaz, azok nem azonosak. Ugyanaz a típus, ha és csak akkor, ha ugyanabból az Assembly példányból származnak.
A dolgok bonyolítása érdekében az ilyen nem egyező típusokkal kapcsolatos kivételüzenetek zavaróak lehetnek. Kivételüzenetekben a típusok egyszerű típusneveikkel vannak megnevezve. Ebben az esetben a gyakori kivételüzenet formája:
Az "IsolatedType" típusú objektum nem konvertálható "IsolatedType" típussá.
Típuskonvertálási problémák hibakeresése
A nem egyező típusokat figyelembe véve fontos tudni a következőket is:
- Az egyes típusok Type.Assembly.
- Az egyes típusok AssemblyLoadContext, amelyek a AssemblyLoadContext.GetLoadContext(Assembly) függvényen keresztül szerezhetők be.
Ha két objektumot a ad meg, és ba hibakeresőben az alábbiak kiértékelése hasznos lesz:
// 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)
Típuskonvertálási problémák megoldása
Az ilyen típusú átalakítással kapcsolatos problémák megoldásához két tervezési minta létezik.
Használjon gyakori megosztott típusokat. Ez a megosztott típus lehet egy primitív futtatókörnyezet-típus, vagy magában foglalhatja egy új megosztott típus létrehozását egy megosztott szerelvényben. A megosztott típus gyakran egy alkalmazásszerelvényben definiált felület . További információkért olvassa el a függőségek megosztásának módját.
Marshalling technikákkal alakíthat át egyik típusról a másikra.
Statikus tagok elérése
Egy egyéni AssemblyLoadContext-be betöltött típusok el vannak különítve a más környezetekben lévő típusoktól, ezért reflexiót kell használnia a statikus tagok eléréséhez a környezeten kívül.
Vegyük például ezt a statikus osztályt egy dinamikusan betöltött szerelvényben:
namespace MyPlugin;
public static class Paths
{
public static DirectoryInfo RootIO { get; private set; }
}
A tulajdonság értékének olvasására használható PropertyInfo.GetValue . Adja meg null első argumentumként, mert a statikus tagoknak nincs szükségük példányra. Adja meg a teljes típusnevet (beleértve a névteret is) a következőnek Assembly.GetType(String):
// Get the type from the loaded assembly using the fully qualified name
Type pathsType = loadedAssembly.GetType("MyPlugin.Paths")
?? throw new InvalidOperationException("Type 'MyPlugin.Paths' not found in loaded assembly.");
// Use PropertyInfo to access a static property
PropertyInfo rootIoProperty = pathsType.GetProperty("RootIO")
?? throw new InvalidOperationException("Property 'RootIO' was not found on type 'MyPlugin.Paths'.");
DirectoryInfo rootIo = (DirectoryInfo)rootIoProperty.GetValue(null);
Másik lehetőségként a C# a tulajdonság-elérőket metódusokba fordítja get_ és set_ prefixekkel. Ezeket a kiegészítő módszereket közvetlenül a következővel Type.GetMethodhívhatja meg: Azonban Type.GetMethod(String) csak nyilvános metódusokat ad vissza. Ha egy tartozék nem nyilvános (például a private set példában), a következőt elfogadó túlterhelést kell használnia BindingFlags:
// Public getter — no BindingFlags needed
MethodInfo getRootIo = pathsType.GetMethod("get_RootIO")
?? throw new InvalidOperationException("Accessor method 'get_RootIO' was not found on type 'MyPlugin.Paths'.");
DirectoryInfo rootIo = (DirectoryInfo)getRootIo.Invoke(null, null);
// Non-public setter — must use BindingFlags
MethodInfo setRootIo = pathsType.GetMethod(
"set_RootIO",
BindingFlags.Static | BindingFlags.NonPublic)
?? throw new InvalidOperationException("Accessor method 'set_RootIO' was not found on type 'MyPlugin.Paths'.");
setRootIo.Invoke(null, new object[] { newValue });
A statikus mezőkre ugyanez a minta vonatkozik, amelyeket a FieldInfo.GetValue és FieldInfo.SetValue segítségével érhet el, valamint a statikus metódusokra, amelyeket a MethodBase.Invoke segítségével hívhat meg.
Megjegyzés:
Ha olyan értéket kér le, amelynek típusa a betöltött assemblyben van definiálva, típuskonvertálási problémákat tapasztalhat, amikor megpróbálja a hívókörnyezetben átalakítani. További információ: Típuskonvertálási problémák.