Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Annotazioni
Questo articolo è specifico di .NET Framework. Non si applica alle implementazioni più recenti di .NET, incluse .NET 6 e versioni successive.
Questo articolo illustra i modi per evitare problemi di identità del tipo che possono causare errori InvalidCastException, MissingMethodException e altri errori. L'articolo illustra le raccomandazioni seguenti:
Comprendere i vantaggi e gli svantaggi dei contesti di carico
Evitare di caricare un assembly in più contesti
Evitare di caricare più versioni di un assembly nello stesso contesto
Prendere in considerazione il passaggio al contesto di caricamento predefinito
La prima raccomandazione, comprendere i vantaggi e gli svantaggi dei contesti di carico, fornisce informazioni di base per le altre raccomandazioni, perché dipendono tutti da una conoscenza dei contesti di carico.
Comprendere i vantaggi e gli svantaggi dei contesti di carico
All'interno di un dominio applicazione, gli assembly possono essere caricati in uno dei tre contesti oppure possono essere caricati senza contesto:
Il contesto di caricamento predefinito contiene assembly trovati attraverso il probing della Global Assembly Cache, il store del host di assembly se il runtime è ospitato (ad esempio, in SQL Server), e il ApplicationBase e PrivateBinPath del dominio dell'applicazione. La maggior parte degli overload del metodo Load caricano gli assembly in questo contesto.
Il contesto di caricamento contiene assembly caricati da percorsi non ricercati dal caricatore. Ad esempio, i componenti aggiuntivi potrebbero essere installati in una directory che non si trova nel percorso dell'applicazione. Assembly.LoadFrom, AppDomain.CreateInstanceFrome AppDomain.ExecuteAssembly sono esempi di metodi che caricano in base al percorso.
Il contesto di sola reflection contiene gli assembly caricati con i metodi ReflectionOnlyLoad e ReflectionOnlyLoadFrom. Il codice in questo contesto non può essere eseguito, quindi non viene illustrato qui. Per altre informazioni, vedere Come caricare assembly nel contesto Reflection-Only.
Se è stato generato un assembly dinamico temporaneo tramite Reflection.Emit, l'assembly non appartiene a nessun contesto. Inoltre, la maggior parte degli assembly caricati tramite il LoadFile metodo viene caricata senza contesto e gli assembly caricati da matrici di byte vengono caricati senza contesto, a meno che l'identità (dopo l'applicazione dei criteri) non stabilisca che si trovano nella Global Assembly Cache.
I contesti di esecuzione presentano vantaggi e svantaggi, come illustrato nelle sezioni seguenti.
Contesto di caricamento predefinito
Quando gli assembly vengono caricati nel contesto di caricamento predefinito, le relative dipendenze vengono caricate automaticamente. Le dipendenze che vengono caricate nel contesto di caricamento predefinito sono trovate automaticamente per gli assembly sia nel contesto di caricamento predefinito che nel contesto di caricamento attraverso un percorso specifico. Il caricamento basato sull'identità dell'assembly aumenta la stabilità delle applicazioni garantendo che non vengano utilizzate versioni sconosciute degli assembly (vedere la sezione Evitare l'associazione su nomi parziali di assembly).
L'uso del contesto di caricamento predefinito presenta gli svantaggi seguenti:
Le dipendenze caricate in altri contesti non sono disponibili.
Non è possibile caricare assembly da posizioni esterne al percorso di probe nel contesto di caricamento predefinito.
contesto Load-From
Il contesto di caricamento consente di caricare un assembly da un percorso non incluso nel percorso dell'applicazione e pertanto non è incluso nel probe. Consente di individuare e caricare le dipendenze da tale percorso, perché le informazioni sul percorso vengono mantenute dal contesto. Inoltre, gli assembly in questo contesto possono utilizzare le dipendenze caricate nel contesto di caricamento predefinito.
Il caricamento di assembly tramite il Assembly.LoadFrom metodo o uno degli altri metodi caricati per percorso presenta gli svantaggi seguenti:
Se un assembly con la stessa identità è già caricato nel contesto di caricamento, LoadFrom restituisce l'assembly caricato anche se è stato specificato un percorso diverso.
Se un assembly viene caricato con LoadFrome successivamente un assembly nel contesto di caricamento predefinito tenta di caricare lo stesso assembly in base al nome visualizzato, il tentativo di caricamento non riesce. Ciò può verificarsi quando un assembly viene deserializzato.
Se un assembly viene caricato con LoadFrom, e il percorso di probing include un assembly con la stessa identità ma in un percorso diverso, può verificarsi un InvalidCastException, MissingMethodException o un altro comportamento imprevisto.
LoadFrom richiede FileIOPermissionAccess.Read e FileIOPermissionAccess.PathDiscovery, o WebPermission, nel percorso specificato.
Se esiste un'immagine nativa per l'assembly, questa non viene utilizzata.
L'assembly non può essere caricato come neutrale rispetto al dominio.
In .NET Framework versioni 1.0 e 1.1 i criteri non vengono applicati.
Nessun contesto
Il caricamento senza contesto è l'unica opzione per gli assembly temporanei generati con reflection emit. Il caricamento senza contesto è l'unico modo per caricare più assembly con la stessa identità in un dominio applicazione. Il costo della sonda viene evitato.
Gli assembly caricati da matrici di byte vengono caricati senza contesto, a meno che l'identità dell'assembly, stabilita quando vengono applicati i criteri, corrisponda all'identità di un assembly nella Global Assembly Cache; in tal caso, l'assembly viene caricato dalla Global Assembly Cache.
Il caricamento di assembly senza contesto presenta gli svantaggi seguenti:
Gli altri assembly non possono essere associati agli assembly caricati senza contesto, a meno che non si gestisca l'evento AppDomain.AssemblyResolve .
Le dipendenze non vengono caricate automaticamente. È possibile precaricarli senza contesto, precaricarli nel contesto di caricamento predefinito o caricarli gestendo l'evento AppDomain.AssemblyResolve .
Il caricamento di più assembly con la stessa identità senza contesto può causare problemi di identità del tipo simili a quelli causati dal caricamento di assembly con la stessa identità in più contesti. Vedere Evitare il caricamento di un assembly in più contesti.
Se esiste un'immagine nativa per l'assembly, questa non viene utilizzata.
L'assembly non può essere caricato come neutrale rispetto al dominio.
In .NET Framework versioni 1.0 e 1.1 i criteri non vengono applicati.
Evitate il collegamento con nomi parziali di assembly
L'associazione di nomi parziali si verifica quando si specifica solo parte del nome visualizzato dell'assembly (FullName) quando si carica un assembly. Ad esempio, è possibile chiamare il metodo Assembly.Load con solo il nome semplice dell'assembly, omettendo la versione, l'impostazione cultura e il token della chiave pubblica. In alternativa, è possibile chiamare il Assembly.LoadWithPartialName metodo , che chiama prima il Assembly.Load metodo e, se non riesce a individuare l'assembly, cerca nella Global Assembly Cache e carica la versione più recente disponibile dell'assembly.
L'associazione di nomi parziali può causare molti problemi, tra cui:
Il Assembly.LoadWithPartialName metodo potrebbe caricare un assembly diverso con lo stesso nome semplice. Ad esempio, due applicazioni possono installare due assembly completamente diversi, entrambi con il "nome semplice"
GraphicsLibrary, nella "Global Assembly Cache".L'assembly attualmente caricato potrebbe non essere compatibile con le versioni precedenti. Ad esempio, non specificare la versione può comportare il caricamento di una versione molto più recente rispetto a quella per cui il programma è stato originariamente scritto. Le modifiche apportate alla versione successiva potrebbero causare errori nell'applicazione.
L'assembly effettivamente caricato potrebbe non essere compatibile con versioni future. Ad esempio, è possibile che l'applicazione sia stata compilata e testata con la versione più recente di un assembly, ma l'associazione parziale potrebbe caricare una versione molto precedente che non dispone delle funzionalità usate dall'applicazione.
L'installazione di nuove applicazioni può interrompere le applicazioni esistenti. Un'applicazione che usa il LoadWithPartialName metodo può essere interrotta installando una versione più recente e incompatibile di un assembly condiviso.
Il caricamento imprevisto delle dipendenze può verificarsi. Se si caricano due assembly che condividono una dipendenza, il loro caricamento con associazione parziale potrebbe comportare che uno degli assembly utilizzi un componente con cui non è stato costruito o testato.
A causa dei problemi che può causare, il LoadWithPartialName metodo è stato contrassegnato come obsoleto. È consigliabile usare il metodo Assembly.Load e specificare nomi completi di visualizzazione dell'assembly. Vedere Comprendere i vantaggi e gli svantaggi dei contesti di carico e prendere in considerazione il passaggio al contesto di caricamento predefinito.
Se desideri utilizzare il metodo LoadWithPartialName perché semplifica il caricamento dell'assembly, considera che far sì che l'applicazione generi un messaggio di errore che identifichi l'assembly mancante probabilmente offre un'esperienza utente migliore rispetto all'uso automatico di una versione sconosciuta dell'assembly, che potrebbe causare comportamenti imprevedibili e vulnerabilità di sicurezza.
Evitare di caricare un assembly in più contesti
Il caricamento di un assembly in più contesti può causare problemi di identità del tipo. Se lo stesso tipo viene caricato dallo stesso assembly in due contesti diversi, è come se fossero stati caricati due tipi diversi con lo stesso nome. Viene generata un'eccezione se si tenta di eseguire il cast di un tipo a un altro, con il messaggio confuso che il tipo InvalidCastException non può essere convertito al tipo MyType.
Si supponga, ad esempio, che l'interfaccia ICommunicate sia dichiarata in un assembly denominato Utility, a cui fa riferimento il programma e anche ad altri assembly caricati dal programma. Questi altri assembly contengono tipi che implementano l'interfaccia ICommunicate , consentendo al programma di usarli.
Considerare ora cosa accade quando viene eseguito il programma. Gli assembly a cui fa riferimento il programma vengono caricati nel contesto di caricamento predefinito. Se carichi un assembly di destinazione sulla base della sua identità, utilizzando il metodo Load, si troverà nel contesto di caricamento predefinito, così come le sue dipendenze. Sia il tuo programma che l'assembly di destinazione utilizzeranno lo stesso assembly Utility.
Si supponga, tuttavia, di caricare l'assembly di destinazione tramite il percorso del file, usando il metodo LoadFile. L'assembly viene caricato senza alcun contesto, quindi le relative dipendenze non vengono caricate automaticamente. Potresti avere un gestore per l'evento AppDomain.AssemblyResolve per fornire la dipendenza, e potrebbe caricare l'assembly Utility senza contesto utilizzando il metodo LoadFile. Ora quando si crea un'istanza di un tipo contenuto nell'assembly di destinazione e si tenta di assegnarlo a una variabile di tipo ICommunicate, viene generata un'eccezione InvalidCastException perché il runtime considera le ICommunicate interfacce nelle due copie dell'assembly Utility come tipi diversi.
Esistono molti altri scenari in cui un modulo/componente può essere caricato in più contesti. L'approccio migliore consiste nell'evitare conflitti spostando l'assembly di destinazione nel percorso dell'applicazione e usando il Load metodo con il nome visualizzato completo. L'assembly viene quindi caricato nel contesto di caricamento predefinito ed entrambi gli assembly usano il medesimo assembly Utility.
Se l'assembly di destinazione deve rimanere all'esterno del percorso dell'applicazione, è possibile usare il LoadFrom metodo per caricarlo nel contesto di caricamento. Se l'assembly di destinazione è stato compilato con un riferimento all'assembly dell'applicazione Utility , userà l'assembly Utility caricato dall'applicazione nel contesto di caricamento predefinito. Si noti che i problemi possono verificarsi se l'assembly di destinazione ha una dipendenza da una copia dell'assembly Utility che si trova al di fuori del percorso dell'applicazione. Se tale assembly viene caricato nel contesto di caricamento prima che l'applicazione carichi l'assembly Utility , il carico dell'applicazione avrà esito negativo.
La sezione Considera il passaggio al contesto di caricamento predefinito discute le alternative all'uso dei caricamenti del percorso del file, ad esempio LoadFile e LoadFrom.
Evitare di caricare più versioni di un assembly nello stesso contesto
Il caricamento di più versioni di un assembly in un unico contesto di caricamento può causare problemi legati all'identità dei tipi. Se lo stesso tipo viene caricato da due versioni dello stesso assembly, è come se fossero stati caricati due tipi diversi con lo stesso nome. Viene generata un'eccezione se si tenta di eseguire il cast di un tipo a un altro, con il messaggio confuso che il tipo InvalidCastException non può essere convertito al tipo MyType.
Ad esempio, il programma potrebbe caricare direttamente una versione dell'assembly Utility e in un secondo momento potrebbe caricare un altro assembly che carica una versione diversa dell'assembly Utility . In alternativa, un errore di codifica potrebbe causare il caricamento di versioni diverse di un assembly in due percorsi di codice diversi nell'applicazione.
Nel contesto di caricamento predefinito questo problema può verificarsi quando si usa il Assembly.Load metodo e si specificano nomi di visualizzazione dell'assembly completi che includono numeri di versione diversi. Per gli assembly caricati senza contesto, il problema può essere causato dall'uso del Assembly.LoadFile metodo per caricare lo stesso assembly da percorsi diversi. Il runtime ritiene che due assembly caricati da percorsi diversi siano assembly differenti, anche se le loro identità sono le medesime.
Oltre ai problemi di identità dei tipi, più versioni di un assembly possono causare un'eccezione MissingMethodException se un tipo caricato da una versione dell'assembly viene passato al codice che prevede tale tipo da una versione diversa. Ad esempio, il codice potrebbe prevedere un metodo aggiunto alla versione successiva.
Se il comportamento del tipo è cambiato tra le versioni, possono verificarsi errori più sottili. Ad esempio, un metodo potrebbe generare un'eccezione imprevista o restituire un valore imprevisto.
Esaminare attentamente il codice per assicurarsi che venga caricata una sola versione di un assembly. È possibile utilizzare il AppDomain.GetAssemblies metodo per determinare quali assembly vengono caricati in qualsiasi momento.
Prendere in considerazione il passaggio al contesto di caricamento predefinito
Esamina i modelli di caricamento e distribuzione dell'assembly dell'applicazione. È possibile eliminare gli assembly caricati da matrici di byte? È possibile spostare gli assembly sul percorso di rilevamento? Se gli assembly si trovano nella Global Assembly Cache o nel percorso di probing del dominio dell'applicazione, ovvero nel suo ApplicationBase e PrivateBinPath, è possibile caricare l'assembly in base alla sua identità.
Se non è possibile inserire tutti gli assembly nel percorso di probe, prendere in considerazione alternative come l'uso del modello di componente aggiuntivo .NET Framework, l'inserimento di assembly nella Global Assembly Cache o la creazione di domini applicativi.
Prendere in considerazione l'uso del modello di Add-In .NET Framework
Se si usa il contesto di caricamento per implementare componenti aggiuntivi, che in genere non sono installati nella base dell'applicazione, usare il modello di componente aggiuntivo .NET Framework. Questo modello fornisce l'isolamento a livello di dominio applicazione o processo, senza che sia necessario gestire manualmente i domini applicazione. Per informazioni sul modello di componente aggiuntivo, vedere Componenti aggiuntivi ed estendibilità.
Prendere in considerazione l'uso della Global Assembly Cache
Posizionare gli assembly nella Global Assembly Cache per ottenere il vantaggio di un percorso di assembly condiviso esterno alla base dell'applicazione, senza perdere i vantaggi del contesto di caricamento predefinito o assumere gli svantaggi degli altri contesti.
Prendere in considerazione l'uso dei domini delle applicazioni
Se si determina che alcuni assembly non possono essere distribuiti nel percorso di probe dell'applicazione, è consigliabile creare un nuovo dominio applicazione per tali assembly. Usare un AppDomainSetup per creare il nuovo dominio applicativo e utilizzare la proprietà AppDomainSetup.ApplicationBase per specificare il percorso che contiene gli assembly da caricare. Se disponi di più directory da sondare, puoi impostare ApplicationBase come directory radice e usare la proprietà AppDomainSetup.PrivateBinPath per identificare le sottodirectory da sondare. In alternativa, è possibile creare più domini applicazione e impostare l'oggetto ApplicationBase di ogni dominio applicazione sul percorso appropriato per i relativi assembly.
Si noti che è possibile usare il Assembly.LoadFrom metodo per caricare questi assembly. Poiché ora si trovano nel percorso di probe, verranno caricati nel contesto di caricamento predefinito anziché nel contesto di caricamento da origine. È tuttavia consigliabile passare al metodo Assembly.Load e specificare nomi completi per la visualizzazione degli assembly per assicurarsi che vengano sempre usate le versioni corrette.