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.
Megjegyzés:
Ez a cikk a .NET-keretrendszerre vonatkozik. Ez nem vonatkozik a .NET újabb implementációira, beleértve a .NET 6-os és újabb verzióit.
Ez a cikk arról szól, hogyan kerülheti el azokat a típusidentitással kapcsolatos problémákat, amelyek InvalidCastException, MissingMethodException és egyéb hibákhoz vezethetnek. A cikk a következő javaslatokat ismerteti:
A terhelési környezetek előnyeinek és hátrányainak megismerése
Kerülje a szerelvény több verziójának betöltését ugyanabba a környezetbe
Fontolja meg az alapértelmezett terhelési környezetre való váltást
Az első javaslat, amely a terhelési környezetek előnyeit és hátrányait ismerteti, háttérinformációkat biztosít a többi javaslathoz, mivel ezek mindegyike a terhelési környezetek ismeretétől függ.
A terhelési környezetek előnyeinek és hátrányainak megismerése
Az alkalmazástartományon belül a szerelvények három környezet egyikébe tölthetők be, vagy környezet nélkül is betölthetők:
Az alapértelmezett betöltési környezet tartalmazza a globális szerelvény-gyorsítótár által megtalált szerelvényeket, a gazdagépi szerelvénytárat, ha a futtatókörnyezet üzemeltetett (például az SQL Serverben), valamint az alkalmazástartomány ApplicationBase és PrivateBinPath összetevőit. A Load metódus legtöbb túlterhelése assembly-ket tölt be ebbe a kontextusba.
A betöltési környezet olyan összeállításokat tartalmazza, amelyek olyan helyekről töltődnek be, amelyeket a betöltő nem keres meg. Előfordulhat például, hogy a bővítmények olyan címtárban vannak telepítve, amely nem az alkalmazás elérési útja alatt található. Assembly.LoadFrom, AppDomain.CreateInstanceFrom és AppDomain.ExecuteAssembly példák az elérési úton betöltő metódusokra.
A csak tükröződési környezet olyan szerelvényeket tartalmaz, amelyeket a ReflectionOnlyLoad és ReflectionOnlyLoadFrom metódusok segítségével töltenek be. Ebben a környezetben a kód nem hajtható végre, ezért itt nem tárgyaljuk. További információkért lásd: Hogyan töltsünk be assembly-kat a Reflection-Only környezetbe.
Ha átmeneti dinamikus összeállítást hozott létre reflection emit használatával, az összeállítás nincs semmilyen kontextusban. Emellett a LoadFile metódussal betöltött szerelvények túlnyomó többsége környezet nélkül töltődik be, és a bájttömbökből betöltött szerelvények is környezet nélkül töltődnek be, kivéve, ha az identitásuk (az irányelv alkalmazása után) megállapítja, hogy a globális szerelvénytárban vannak.
A végrehajtási környezeteknek vannak előnyei és hátrányai, amint az a következő szakaszokban is szerepel.
Alapértelmezett terhelési környezet
Amikor a szerelvények betöltődnek az alapértelmezett betöltési környezetbe, a rendszer automatikusan betölti a függőségeket. Az alapértelmezett betöltési környezetbe betöltött függőségek automatikusan megtalálhatók az alapértelmezett betöltési környezetben vagy a load-from környezetben lévő összeállításoknál. A szerelvények identitása szerinti betöltés növeli az alkalmazások stabilitását azáltal, hogy biztosítja, hogy a szerelvények ismeretlen verziói ne legyenek használatban (lásd a Részleges szerelvénynevek kötésének elkerülése című szakaszt).
Az alapértelmezett terhelési környezet használata a következő hátrányokkal rendelkezik:
Azok a függőségek, amelyek más környezetekbe vannak betöltve, nem érhetők el.
A keresési útvonalon kívüli helyekről nem tölthet be assembly-ket az alapértelmezett betöltési környezetbe.
Load-From környezet
A betöltési forrás kontextus lehetővé teszi, hogy egy olyan elérési útról töltsön be egy összetevőt, amely nincs az alkalmazás elérési útja alatt, és ezért nem szerepel a keresési folyamatban. Lehetővé teszi a függőségek elhelyezését és betöltését ebből az elérési útból, mivel az elérési út adatait a környezet tartja karban. Emellett az összeállítások ebben a kontextusban az alapértelmezett betöltési környezetbe betöltött függőségeket is használhatják.
Az összeállítások betöltése a Assembly.LoadFrom módszerrel, vagy a fájl útvonallal betöltő többi módszer egyikének a következő hátrányai vannak:
Ha egy azonos identitással rendelkező összeállítás már be van töltve a betöltésből származó környezetbe, LoadFrom akkor is visszaadja a betöltött összeállítást, ha más elérési út van megadva.
Ha egy összeállítás be van töltve LoadFrom, és később az alapértelmezett betöltési környezetben egy összeállítás megpróbálja betölteni ugyanazt az összeállítást megjelenítési névvel, a betöltési kísérlet sikertelen lesz. Ez akkor fordulhat elő, ha egy szerelvény deszerializálva van.
Ha egy szerelvényt LoadFrom betöltenek, és a próbaútvonal tartalmaz egy azonos identitású, de más helyen lévő szerelvényt, akkor InvalidCastException, MissingMethodException vagy más váratlan viselkedés léphet fel.
LoadFrom FileIOPermissionAccess.Read és FileIOPermissionAccess.PathDiscovery, vagy WebPermission, a megadott útvonalon.
Ha létezik natív kép az összeszereléshez, azt nem használják.
Az assembly nem tölthető be tartománysemlegesként.
A .NET-keretrendszer 1.0-s és 1.1-s verzióiban a szabályzat nem lesz alkalmazva.
Nincs kontextus
Környezet nélküli betöltés az egyetlen lehetőség az ideiglenes assembly-khez, amelyeket reflectáló kibocsátással hoznak létre. A környezet nélküli betöltés az egyetlen módja annak, hogy több olyan assemblyt töltsön be, amelyek ugyanazzal az identitással rendelkeznek egy alkalmazási tartományba. A feltárás költsége elkerülhető.
A bájttömbökből betöltött szerelvények kontextus nélkül töltődnek be, kivéve, ha a szabályzat alkalmazásakor létrehozott szerelvény identitása megegyezik egy szerelvény identitásával a globális szerelvény-gyorsítótárban; ebben az esetben a rendszer betölti a szerelvényt a globális szerelvény-gyorsítótárból.
Az összeállítások környezet nélküli betöltésének a következő hátrányai vannak:
Más szerelvények nem tudnak kapcsolódni a kontextus nélkül betöltött szerelvényekhez, kivéve, ha kezeli a AppDomain.AssemblyResolve eseményt.
A függőségek nem töltődnek be automatikusan. Előre betöltheti őket környezet nélkül, előre betöltheti őket az alapértelmezett betöltési környezetbe, vagy betöltheti őket az AppDomain.AssemblyResolve esemény kezelésével.
Ha több, azonos identitással rendelkező szerelvényt tölt be környezet nélkül, az típusidentitás-problémákat okozhat, amelyek hasonlóak azokhoz a problémákhoz, amelyeket az azonos identitással rendelkező szerelvények több környezetbe való betöltése okoz. Lásd, Kerülje el a szerelvény betöltését több környezetbe.
Ha létezik natív kép az összeszereléshez, azt nem használják.
Az assembly nem tölthető be tartománysemlegesként.
A .NET-keretrendszer 1.0-s és 1.1-s verzióiban a szabályzat nem lesz alkalmazva.
Kerülje a részleges összeszerelési nevekhez való kötődést
Részleges névkötés akkor fordul elő, ha az összeállítás betöltésekor az összeállítás megjelenítendő nevének (FullName) csak egy részét adja meg. Meghívhatja például a Assembly.Load metódust csak az összetevő egyszerű nevével, kihagyva a verziót, a kultúrát és a nyilvános kulcs tokenjét. Vagy meghívhatja a Assembly.LoadWithPartialName metódust, amely először meghívja a Assembly.Load metódust, és ha ez nem találja a szerelvényt, megkeresi a globális szerelvény gyorsítótárát, és betölti a szerelvény legújabb elérhető verzióját.
A részleges névkötés számos problémát okozhat, többek között az alábbiakat:
Előfordulhat, hogy a Assembly.LoadWithPartialName metódus egy másik szerelvényt tölt be ugyanazzal az egyszerű névvel. Például két alkalmazás két teljesen különböző szerelvényt telepíthet, amelyek mindegyike a globális szerelvény-gyorsítótárban az egyszerű nevet
GraphicsLibraryviseli.Előfordulhat, hogy a ténylegesen betöltött szerelvény nem kompatibilis visszamenőlegesen. Ha például nem adja meg a verziót, az egy sokkal későbbi verzió betöltését eredményezheti, mint a program eredetileg használni kívánt verziója. A későbbi verzió módosításai hibákat okozhatnak az alkalmazásban.
Előfordulhat, hogy a ténylegesen betöltött összetevő nem lesz kompatibilis a jövőbeli verziókkal. Előfordulhat például, hogy az alkalmazást egy szerelvény legújabb verziójával hozta létre és tesztelte, de a részleges kötés betölthet egy sokkal korábbi verziót, amely nem rendelkezik az alkalmazás által használt funkciókkal.
Az új alkalmazások telepítése megszakíthatja a meglévő alkalmazásokat. A metódust használó LoadWithPartialName alkalmazások megszakadhatnak egy megosztott szerelvény újabb, nem kompatibilis verziójának telepítésével.
Váratlan függőségbetöltés történhet. Ha két összeállítást tölt be, amelyek osztoznak egy függőségen, a részleges összekapcsolás esetén az egyik összeállítás egy olyan összetevőt használhat, amellyel nem készült vagy tesztelték.
Az általa okozott problémák miatt a LoadWithPartialName metódus elavultnak lett jelölve. Javasoljuk, hogy inkább a metódust Assembly.Load használja, és adja meg a teljes szerelvénymegjelenítési neveket. Tekintse meg a terhelési környezetek előnyeit és hátrányait , és fontolja meg az alapértelmezett terhelési környezetre való váltást.
Ha a LoadWithPartialName metódust szeretné használni, mert megkönnyíti az assembly betöltését, vegye figyelembe, hogy egy hibaüzenet, amely azonosítja a hiányzó assemblyt, valószínűleg jobb felhasználói élményt nyújt, mintha automatikusan egy ismeretlen verziót használna, ami kiszámíthatatlan viselkedést és biztonsági kockázatokat okozhat.
Ne töltsünk be egy összeállítást több környezetbe
Egy szerelvény több környezetbe való betöltése típusidentitási problémákat okozhat. Ha ugyanabból a szerelvényből két különböző környezetbe tölti be ugyanazt a típust, az olyan, mintha két azonos nevű típus lett volna betöltve. Egy InvalidCastException akkor keletkezik, ha megpróbálunk egy típust a másikba konvertálni, és egy zavaró üzenetet kapunk, hogy a MyType típus nem konvertálható a MyType típusra.
Tegyük fel például, hogy az ICommunicate interfész egy nevesített Utilityszerelvényben van deklarálva, amelyre a program hivatkozik, valamint a program által betöltött egyéb szerelvényekre is. Ezek a többi szerelvények olyan típusokat tartalmaznak, amelyek implementálják az ICommunicate interfészt, lehetővé téve a program számára, hogy használja őket.
Most gondolja át, mi történik a program futtatásakor. A program által hivatkozott assembly-k az alapértelmezett betöltési környezetbe betöltődnek. Ha egy célszerelvényt az identitása alapján tölt be a Load metódus használatával, az az alapértelmezett betöltési környezetben lesz, és a függőségei is. A program és a célszerelvény is ugyanazt Utility a szerelvényt fogja használni.
Tegyük fel azonban, hogy a metódus használatával betölti a célszerelvényt a LoadFile fájl elérési útján. A szerelvény környezet nélkül van betöltve, így a rendszer nem tölti be automatikusan a függőségeit. Lehetséges, hogy van egy kezelő az AppDomain.AssemblyResolve eseményhez, amely biztosítja a függőséget, és a Utility metódus használatával környezet nélkül tölti be a LoadFile összeállítást. Most, amikor létrehoz egy példányt egy olyan típusból, amely a célobjektum-összeállításban található, és megpróbálja hozzárendelni egy ICommunicate típusú változóhoz, akkor InvalidCastException kivételt dob, mert a futtatókörnyezet a ICommunicate objektum-összeállítás két példányában található Utility interfészeket különböző típusoknak tekinti.
Számos egyéb forgatókönyv létezik, amikor egy programösszetevő több környezetbe is betölthető. A legjobb módszer az ütközések elkerülésére, ha áthelyezi a célösszeállítást az alkalmazás elérési útjára, és a Load módszert a teljes megjelenítési névvel használja. A rendszer ezután betölti a szerelvényt az alapértelmezett terhelési környezetbe, és mindkét szerelvények ugyanazt a szerelvényt Utility használják.
Ha a célszerelvénynek az alkalmazás elérési útján kívül kell megmaradnia, a LoadFrom metódus használatával betöltheti a load-from környezetbe. Ha a célszerelvényt az alkalmazás szerelvényére Utility mutató hivatkozással állították össze, azt a Utility szerelvényt fogja használni, amelyet az alkalmazás betöltött az alapértelmezett betöltési környezetbe. Vegye figyelembe, hogy problémák akkor fordulhatnak elő, ha a célszerelvény függ az Utility alkalmazás elérési útján kívül található szerelvény egy példányától. Ha a szerelvény betöltődik a betöltési környezetbe, mielőtt az alkalmazás betöltené a Utility szerelvényt, az alkalmazás betöltése sikertelen lesz.
Az Alapértelmezett betöltési környezetre váltás lehetőség című szakasz ismerteti a fájlelérési utak használatának alternatíváit, mint például LoadFile és LoadFrom.
Kerülje a szerelvény több verziójának betöltését ugyanabba a környezetbe
Egy szerelvény több verziójának betöltése egy betöltési környezetbe típusidentitás-problémákat okozhat. Ha ugyanazt a típust ugyanazon szerelvény két verziójából tölti be, az olyan, mintha két azonos nevű különböző típus lett volna betöltve. Egy InvalidCastException akkor keletkezik, ha megpróbálunk egy típust a másikba konvertálni, és egy zavaró üzenetet kapunk, hogy a MyType típus nem konvertálható a MyType típusra.
Előfordulhat például, hogy a program közvetlenül betölti a Utility szerelvény egyik verzióját, később pedig betölt egy másik szerelvényt, amely betölti a Utility szerelvény egy másik verzióját. Vagy kódolási hiba okozhatja, hogy az alkalmazásban két különböző kódútvonal különféle verziójú összetevőket tölt be.
Az alapértelmezett betöltési környezetben ez a probléma akkor fordulhat elő, amikor a Assembly.Load metódust használja, és megad teljes szerelvénymegjelenítési neveket, amelyek különböző verziószámokat tartalmaznak. Azoknál az összeállításoknál, amelyek környezet nélkül kerülnek betöltésre, a problémát okozhatja, ha a Assembly.LoadFile metódussal ugyanazt az összeállítást különböző útvonalakról tölti be. A futtatókörnyezet a különböző útvonalakról betöltött két szerelvényt eltérő szerelvénynek tekinti, még akkor is, ha azonosak az azonosítóik.
A típusazonosság problémái mellett a szerelvények több verziója okozhat problémát, ha egy típus, amelyet a szerelvény egyik verziójáról töltenek be, olyan kódhoz kerül, amely azt a típust egy másik verzióból várja. Előfordulhat például, hogy a kód egy metódust vár, amelyet hozzáadtak a későbbi verzióhoz.
Finomabb hibák akkor fordulhatnak elő, ha a típus viselkedése a verziók között megváltozott. Előfordulhat például, hogy egy metódus váratlan kivételt jelez, vagy váratlan értéket ad vissza.
Gondosan tekintse át a kódot, hogy a szerelvénynek csak egy verziója legyen betöltve. Ezzel a AppDomain.GetAssemblies módszerrel meghatározhatja, hogy mely szerelvények tölthetők be egy adott időpontban.
Fontolja meg az alapértelmezett terhelési környezetre való váltást
Vizsgálja meg az alkalmazás szerelvénybetöltési és üzembehelyezési mintáit. Meg tudja szüntetni a bájttömbökből betöltött assemblyket? Át tudja helyezni a szerelvényeket a vizsgálati útvonalra? Ha a szerelvények a globális szerelvénygyorsítótárban vagy az alkalmazástartomány próbaútvonalán (azaz annak ApplicationBase és PrivateBinPath) találhatók, a szerelvényt az identitása alapján betöltheti.
Ha nem lehet az összes szerelvényt a próbaúton elhelyezni, fontolja meg az olyan alternatív megoldásokat, mint a .NET-keretrendszer bővítménymodelljének használata, a szerelvények globális szerelvény-gyorsítótárba helyezése vagy alkalmazástartományok létrehozása.
Fontolja meg a .NET-keretrendszer Add-In modell használatát
Ha a betöltési kontextust használja a bővítmények implementálásához, amelyek általában nincsenek telepítve az alkalmazásbázisban, akkor használja a .NET-keretrendszer bővítmény modelljét. Ez a modell elkülönítést biztosít az alkalmazástartomány vagy a folyamat szintjén anélkül, hogy saját maga kellene kezelnie az alkalmazástartományokat. A bővítménymodellről további információt a Bővítmények és bővíthetőség című témakörben talál.
Fontolja meg a globális assembly gyorsítótár használatát
Helyezze a szerelvényeket a globális szerelvény-gyorsítótárba, hogy élvezhesse az alkalmazásbázison kívüli megosztott szerelvényútvonal előnyeit anélkül, hogy elveszítené az alapértelmezett terhelési környezet nyújtotta előnyöket vagy szembesülne a többi környezet hátrányaival.
Fontolja meg az alkalmazástartományok használatát
Ha úgy ítéli meg, hogy egyes szerelvények nem helyezhetők üzembe az alkalmazás próbaútvonalán, érdemes lehet létrehozni egy új alkalmazástartományt ezekhez a szerelvényekhez. Az AppDomainSetup használatával hozza létre az új alkalmazási tartományt, és a AppDomainSetup.ApplicationBase tulajdonság használatával adja meg a betölteni kívánt szerelvények elérési útját. Ha több könyvtárat szeretne mintavételezni, beállíthatja a ApplicationBase gyökérkönyvtárat, és a AppDomainSetup.PrivateBinPath tulajdonság használatával azonosíthatja a mintavételhez használt alkönyvtárakat. Másik lehetőségként több alkalmazásdomaint is létrehozhat, és az alkalmazásdomainek ApplicationBase-jait a szerelvények megfelelő elérési útjára állíthatja.
Vegye figyelembe, hogy a Assembly.LoadFrom módszert használhatja ezeknek az assembly-knek a betöltésére. Mivel most már a próbaútvonalon vannak, a rendszer betölti őket az alapértelmezett terhelési környezetbe a betöltési környezet helyett. Javasoljuk azonban, hogy váltson a Assembly.Load metódusra, és adja meg a teljes assembly megjelenítési neveket, hogy mindig a megfelelő verziókat használják.