Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Remarque
Cet article est spécifique à .NET Framework. Elle ne s’applique pas aux implémentations plus récentes de .NET, notamment .NET 6 et versions ultérieures.
Cet article décrit les façons d’éviter les problèmes d’identité de type pouvant entraîner InvalidCastException, MissingMethodExceptionet d’autres erreurs. L’article décrit les recommandations suivantes :
Comprendre les avantages et les inconvénients des contextes de charge
Évitez de charger plusieurs versions d’un assembly dans le même contexte
La première recommandation, comprendre les avantages et les inconvénients des contextes de charge, fournit des informations générales sur les autres recommandations, car elles dépendent toutes d’une connaissance des contextes de charge.
Comprendre les avantages et les inconvénients des contextes de charge
Dans un domaine d’application, les assemblys peuvent être chargés dans l’un des trois contextes, ou ils peuvent être chargés sans contexte :
Le contexte de chargement par défaut contient des assemblages trouvés en explorant le cache global d'assemblage, le magasin d'assemblages hôte si l'environnement d'exécution est hébergé (par exemple, dans SQL Server), et le domaine d'application ApplicationBasePrivateBinPath. La plupart des surcharges de la méthode Load chargent les assemblys dans ce contexte.
Le contexte de chargement contient des assemblies chargées à partir d’emplacements non recherchés par le chargeur. Par exemple, les compléments peuvent être installés dans un répertoire qui ne se trouve pas sous le chemin d’accès de l’application. Assembly.LoadFrom, AppDomain.CreateInstanceFromet AppDomain.ExecuteAssembly sont des exemples de méthodes qui chargent par chemin d’accès.
Le contexte de réflexion uniquement contient des assemblys chargés avec les méthodes ReflectionOnlyLoad et ReflectionOnlyLoadFrom. Le code dans ce contexte ne peut pas être exécuté. Il n’est donc pas abordé ici. Pour plus d’informations, consultez Comment charger des assemblages dans le contexte Reflection-Only.
Si vous avez généré un assemblage dynamique transitoire par émission de réflexion, l’assemblage ne s’inscrit dans aucun contexte. En outre, la plupart des assemblys chargés à l’aide de la LoadFile méthode sont chargés sans contexte, et les assemblys chargés à partir de tableaux d’octets sont chargés sans contexte, sauf si leur identité (après l’application de la stratégie) établit qu’elles se trouvent dans le Global Assembly Cache.
Les contextes d’exécution présentent des avantages et des inconvénients, comme indiqué dans les sections suivantes.
Contexte de chargement par défaut
Lorsque les assemblys sont chargés dans le contexte de chargement par défaut, leurs dépendances sont chargées automatiquement. Les dépendances qui sont chargées dans le contexte de chargement par défaut sont trouvées automatiquement pour les assemblys dans le contexte de chargement par défaut ou dans le contexte de chargement source. Le chargement par identité d’assembly augmente la stabilité des applications en vérifiant que des versions inconnues des assemblys ne sont pas utilisées (consultez la section Éviter la liaison sur les noms d’assemblys partiels).
L’utilisation du contexte de chargement par défaut présente les inconvénients suivants :
Les dépendances chargées dans d’autres contextes ne sont pas disponibles.
Vous ne pouvez pas charger des assemblages à partir d’emplacements en dehors du chemin de recherche dans le contexte de chargement par défaut.
Contexte de chargement source
Le contexte de chargement vous permet de charger un assembly depuis un chemin qui n'est pas situé sous le chemin d'accès de l'application et qui n'est donc pas inclus dans la recherche. Il permet aux dépendances d’être localisées et chargées depuis ce chemin, car les informations du chemin sont conservées par le contexte. En outre, les assemblages dans ce contexte peuvent utiliser des dépendances chargées dans le contexte de chargement par défaut.
Le chargement d'assemblages à l'aide de la méthode Assembly.LoadFrom, ou l'une des autres méthodes qui chargent par chemin, présente les inconvénients suivants :
Si un assembly avec la même identité est déjà chargé dans le contexte de chargement, LoadFrom retourne l’assembly chargé même si un chemin d’accès différent a été spécifié.
Si un assembly est chargé avec LoadFrom, et ultérieurement un assembly dans le contexte de chargement par défaut tente de charger le même assembly par nom d’affichage, la tentative de chargement échoue. Cela peut se produire lorsqu’un module est désérialisé.
Si un assemblage est chargé avec LoadFrom, et que le chemin de recherche inclut un assemblage avec la même identité, mais dans un emplacement différent, un InvalidCastException, MissingMethodException ou un autre comportement inattendu peut se produire.
LoadFrom demande FileIOPermissionAccess.Read et FileIOPermissionAccess.PathDiscovery, ou WebPermission, sur le chemin spécifié.
Si une image native existe pour l’assembly, elle n’est pas utilisée.
L’assembly ne peut pas être chargé comme étant indépendant du domaine.
Dans .NET Framework versions 1.0 et 1.1, la stratégie n’est pas appliquée.
Aucun contexte
Le chargement sans contexte est la seule option pour les assemblages temporaires générés avec la réflexion émise. Le chargement sans contexte est le seul moyen de charger plusieurs assemblys qui ont la même identité dans un domaine d’application. Le coût de la détection est évité.
Les assemblys chargés à partir de tableaux d’octets sont chargés sans contexte, sauf si l’identité de l’assembly, établie lorsque la stratégie est appliquée, correspond à l’identité d’un assembly dans le Global Assembly Cache ; dans ce cas, l’assembly est chargé à partir du Global Assembly Cache.
Le chargement d’assemblages sans contexte présente les inconvénients suivants :
Les autres assemblies ne peuvent pas lier des assemblies qui sont chargées sans contexte, à moins que vous ne gériez l’événement AppDomain.AssemblyResolve.
Les dépendances ne sont pas chargées automatiquement. Vous pouvez les précharger sans contexte, les précharger dans le contexte de chargement par défaut ou les charger en gérant l’événement AppDomain.AssemblyResolve .
Le chargement de plusieurs assemblys avec la même identité sans contexte peut entraîner des problèmes d’identité de type similaires à ceux causés par le chargement d’assemblys avec la même identité dans plusieurs contextes. Consultez Éviter le chargement d’un assemblage dans plusieurs contextes.
Si une image native existe pour l’assembly, elle n’est pas utilisée.
L’assembly ne peut pas être chargé comme étant indépendant du domaine.
Dans .NET Framework versions 1.0 et 1.1, la stratégie n’est pas appliquée.
Éviter la liaison sur les noms d’assemblys partiels
La liaison de nom partiel se produit lorsque vous spécifiez uniquement une partie du nom affiché de l’assemblage (FullName) en chargeant un assemblage. Par exemple, vous pouvez appeler la méthode Assembly.Load avec uniquement le nom simplifié de l’assembly, tout en omettant la version, la culture et le jeton de clé publique. Vous pouvez également appeler la Assembly.LoadWithPartialName méthode, qui appelle d’abord la Assembly.Load méthode et, si cela ne parvient pas à localiser l’assembly, recherche le Global Assembly Cache et charge la dernière version disponible de l’assembly.
La liaison partielle de noms peut entraîner de nombreux problèmes, notamment les suivants :
La Assembly.LoadWithPartialName méthode peut charger un autre assembly avec le même nom simple. Par exemple, deux applications peuvent installer deux assemblys complètement différents qui ont tous deux le nom
GraphicsLibrarysimple dans le Global Assembly Cache.L’assembly qui est actuellement chargé risque de ne pas être à compatibilité descendante. Par exemple, le fait de ne pas spécifier la version peut entraîner le chargement d’une version beaucoup plus tard que la version que votre programme a été écrite à l’origine pour l’utiliser. Les modifications apportées à la version ultérieure peuvent entraîner des erreurs dans votre application.
L’assembly qui est actuellement chargé risque de ne pas être à compatibilité ascendante. Par exemple, vous avez peut-être créé et testé votre application avec la dernière version d’un assembly, mais une liaison partielle peut charger une version beaucoup plus ancienne qui manque de fonctionnalités que votre application utilise.
L’installation de nouvelles applications peut interrompre les applications existantes. Une application qui utilise la LoadWithPartialName méthode peut être interrompue en installant une version plus récente et incompatible d’un assembly partagé.
Le chargement d’une dépendance inattendue peut se produire. Si vous chargez deux assemblies qui partagent une dépendance et que vous les chargez avec une liaison partielle, cela pourrait entraîner l’utilisation d’un assembly en utilisant un composant avec lequel il n’a pas été conçu ou testé.
En raison des problèmes qu’elle peut provoquer, la LoadWithPartialName méthode a été marquée comme obsolète. Nous vous recommandons d’utiliser à la place la méthode Assembly.Load et de spécifier des noms d’affichage d’assemblys complets. Consultez Comprendre les avantages et inconvénients des contextes de charge et envisager de basculer vers le contexte de charge par défaut.
Si vous souhaitez utiliser la méthode LoadWithPartialName parce qu'elle facilite le chargement de l'assembly, il vaut mieux que votre application échoue avec un message d’erreur identifiant l’assembly manquant. Cela est susceptible de fournir une meilleure expérience utilisateur que l’utilisation automatique d’une version inconnue de l’assembly, ce qui peut entraîner un comportement imprévisible et des failles de sécurité.
Éviter de charger un assembly dans plusieurs contextes
Le chargement d’un assembly dans plusieurs contextes peut provoquer des problèmes d’identités de type. Si le même type est chargé à partir du même assembly dans deux contextes différents, c’est comme si deux types différents portant le même nom avaient été chargés. Une InvalidCastException est levée si vous essayez d’effectuer un cast d’un type à l’autre, avec un message trompeur, selon lequel le type MyType ne peut pas être converti en type MyType.
Par exemple, supposons que l’interface ICommunicate est déclarée dans un assembly nommé Utility, qui est référencé par votre programme et également par d’autres assemblys que votre programme charge. Ces autres assemblys contiennent des types qui implémentent l’interface ICommunicate , ce qui permet à votre programme de les utiliser.
Considérez maintenant ce qui se passe quand votre programme est exécuté. Les assemblys référencés par votre programme sont chargés dans le contexte de chargement par défaut. Si vous chargez un assembly cible par son identité, à l'aide de la méthode Load, il se trouve dans le contexte de chargement par défaut, ainsi que ses dépendances. Votre programme et l’assembly cible utilisent le même Utility assembly.
Supposez cependant que vous chargez l’assembly cible via son chemin de fichier en utilisant la méthode LoadFile. L’assembly est chargé sans contexte, de sorte que ses dépendances ne sont pas automatiquement chargées. Vous pouvez avoir un gestionnaire pour l’événement AppDomain.AssemblyResolve pour fournir la dépendance, et il peut charger l’assembly Utility sans contexte à l’aide de la LoadFile méthode. Quand vous créez une instance d’un type qui est contenu dans l’assembly cible et que vous essayez de l’affecter à une variable de type ICommunicate, une InvalidCastException est levée, car le runtime considère que les interfaces ICommunicate dans les deux copies de l’assembly Utility sont des types différents.
Il existe de nombreux autres scénarios dans lesquels un assembly peut être chargé dans plusieurs contextes. La meilleure approche consiste à éviter les conflits en déplaçant l’assembly cible dans le chemin d’accès de votre application et en utilisant la Load méthode avec le nom complet d’affichage. L’assembly est ensuite chargé dans le contexte de chargement par défaut, et les deux assemblys utilisent le même Utility assembly.
Si l’assembly cible doit rester en dehors du chemin d’accès de votre application, vous pouvez utiliser la méthode LoadFrom pour la charger dans le contexte load-from. Si l’assembly cible a été compilé avec une référence à l’assembly de Utility votre application, il utilise l’assembly Utility que votre application a chargé dans le contexte de chargement par défaut. Notez que des problèmes peuvent se produire si l’assembly cible a une dépendance sur une copie de l’assembly situé en dehors de votre chemin d’accès à l’application Utility . Si cet assembly est chargé dans le contexte de chargement avant que votre application tente de charger l'assembly Utility, le chargement de votre application échouera.
La section Envisager de passer au Contexte de chargement par défaut décrit les alternatives à l'utilisation des chargements de chemins d'accès de fichier, comme LoadFile et LoadFrom.
Éviter de charger plusieurs versions d’un assembly dans le même contexte
Le chargement de plusieurs versions d’un assembly dans un contexte de chargement peut entraîner des problèmes d’identité de type. Si le même type est chargé à partir de deux versions du même assembly, c’est comme si deux types différents portant le même nom avaient été chargés. Une InvalidCastException est levée si vous essayez d’effectuer un cast d’un type à l’autre, avec un message trompeur, selon lequel le type MyType ne peut pas être converti en type MyType.
Par exemple, votre programme peut charger directement une version de l’assembly Utility , puis charger un autre assembly qui charge une autre version de l’assembly Utility . Ou une erreur de codage peut entraîner deux chemins de code différents dans votre application pour charger différentes versions d’un assembly.
Dans le contexte de chargement par défaut, ce problème peut se produire lorsque vous utilisez la Assembly.Load méthode et spécifiez des noms complets d’affichage d’assembly qui incluent différents numéros de version. Pour les assemblys chargés sans contexte, le problème peut être dû à l’utilisation de la Assembly.LoadFile méthode pour charger le même assembly à partir de différents chemins d’accès. Le runtime considère deux assemblages chargés à partir de chemins différents comme des assemblages distincts, même si leurs identités sont identiques.
En plus des problèmes d’identité de type, plusieurs versions d’un assembly peuvent entraîner une MissingMethodException erreur si un type chargé à partir d’une version de l’assembly est passé au code qui attend ce type d’une autre version. Par exemple, le code peut s’attendre à une méthode ajoutée à la version ultérieure.
Des erreurs plus subtiles peuvent se produire si le comportement du type a changé entre les versions. Par exemple, une méthode peut lever une exception inattendue ou retourner une valeur inattendue.
Examinez soigneusement votre code pour vous assurer qu’une seule version d’un assembly est chargée. Vous pouvez utiliser la AppDomain.GetAssemblies méthode pour déterminer quels assemblys sont chargés à tout moment.
Envisagez de passer au contexte de chargement par défaut
Examinez les stratégies de chargement et de déploiement des assemblages de votre application. Pouvez-vous supprimer les assemblies chargées depuis des tableaux d’octets ? Pouvez-vous déplacer des assemblys dans le chemin de recherche ? Si des assemblies se trouvent dans le Global Assembly Cache ou dans le chemin de recherche du domaine d'application (autrement dit, sa ApplicationBase et PrivateBinPath), vous pouvez charger l'assembly par son identité.
S’il n’est pas possible de placer tous vos assemblys dans le chemin de recherche, considérez les solutions alternatives, comme utiliser le modèle de complément du .NET Framework, placer les assemblys dans le Global Assembly Cache ou créer des domaines d’application.
Envisagez d’utiliser le modèle Add-In .NET Framework
Si vous utilisez le contexte de chargement pour implémenter des compléments, qui ne sont généralement pas installés dans la base de l’application, utilisez le modèle de complément .NET Framework. Ce modèle fournit une isolation au niveau du domaine d’application ou du processus, sans avoir à gérer vous-même les domaines d’application. Pour plus d’informations sur le modèle de complément, consultez Compléments et Extensibilité.
Envisagez d’utiliser le Global Assembly Cache
Placez des assemblys dans le Global Assembly Cache pour tirer parti d’un chemin d’assembly partagé qui se trouve en dehors de la base de l’application, sans perdre les avantages du contexte de chargement par défaut ou prendre les inconvénients des autres contextes.
Envisager d’utiliser des domaines d’application
Si vous déterminez que certains de vos assemblys ne peuvent pas être déployés dans le chemin de recherche de l’application, envisagez de créer un domaine d’application pour ces assemblys. Utilisez un AppDomainSetup pour créer le domaine d’application et utilisez la AppDomainSetup.ApplicationBase propriété pour spécifier le chemin d’accès qui contient les assemblys que vous souhaitez charger. Si vous avez plusieurs répertoires à sonder, vous pouvez définir le ApplicationBase répertoire racine et utiliser la AppDomainSetup.PrivateBinPath propriété pour identifier les sous-répertoires à sonder. Vous pouvez aussi créer plusieurs domaines d’application et définir ApplicationBase pour chaque domaine d’application sur le chemin approprié pour ses assemblys.
Notez que vous pouvez utiliser la Assembly.LoadFrom méthode pour charger ces assemblys. Comme ils sont maintenant dans le chemin de recherche, ils seront chargés dans le contexte de chargement par défaut au lieu du contexte de chargement source. Cependant, nous vous recommandons de passer à la méthode Assembly.Load et de spécifier des noms d’affichage d’assemblys complets pour garantir que les versions correctes sont toujours utilisées.