Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
Este artigo é específico do .NET Framework. Ele não se aplica a implementações mais recentes do .NET, incluindo o .NET 6 e versões posteriores.
Este artigo discute maneiras de evitar problemas de identidade de tipo que podem levar a InvalidCastException, MissingMethodExceptione outros erros. O artigo discute as seguintes recomendações:
Compreender as vantagens e desvantagens dos contextos de carga
Evite carregar várias versões de um assembly no mesmo contexto
A primeira recomendação, compreender as vantagens e desvantagens dos contextos de carga, fornece informações de fundo para as outras recomendações, porque todas elas dependem de um conhecimento dos contextos de carga.
Compreender as vantagens e desvantagens dos contextos de carga
Dentro de um domínio de aplicativo, os assemblies podem ser carregados em um dos três contextos ou podem ser carregados sem contexto:
O contexto de carregamento padrão contém conjuntos encontrados ao examinar o cache global de conjuntos, o armazenamento de conjuntos do anfitrião quando o tempo de execução é hospedado (por exemplo, no SQL Server) e o ApplicationBase e PrivateBinPath do domínio do aplicativo. A maioria das sobrecargas do método Load carrega conjuntos de programas nesse contexto.
O contexto load-from contém assemblies que são carregados de locais que não são pesquisados pelo carregador. Por exemplo, os complementos podem ser instalados numa diretoria que não esteja fora do caminho da aplicação. Assembly.LoadFrom, AppDomain.CreateInstanceFrome AppDomain.ExecuteAssembly são exemplos de métodos que carregam por caminho.
O contexto apenas reflexão contém assemblies carregados com os métodos ReflectionOnlyLoad e ReflectionOnlyLoadFrom. O código neste contexto não pode ser executado, por isso não é discutido aqui. Para obter mais informações, consulte Como carregar assemblies no contexto Reflection-Only.
Se tiver gerado um assembly dinâmico transitório usando Reflection.Emit, o assembly não se encontra em nenhum contexto. Além disso, a maioria das assemblagens que são carregadas usando o LoadFile método são carregadas sem contexto, e assemblagens que são carregadas a partir de matrizes de bytes são carregadas sem contexto, a menos que sua identidade (após a aplicação da política) estabeleça que estão no cache global de assemblagens.
Os contextos de execução têm vantagens e desvantagens, conforme discutido nas seções a seguir.
Contexto de carregamento padrão
Quando os assemblies são carregados no contexto de carga padrão, suas dependências são carregadas automaticamente. As dependências que são carregadas no contexto de carga padrão são automaticamente identificadas para conjuntos no contexto de carga padrão ou no contexto de carga alternativa. O carregamento por identidade de assembly aumenta a estabilidade das aplicações, assegurando que versões desconhecidas de assemblies não sejam utilizadas (consulte a seção Evitar associação em nomes parciais de assemblies).
Usar o contexto de carga padrão tem as seguintes desvantagens:
As dependências que são carregadas em outros contextos não estão disponíveis.
Não é possível carregar assemblies de locais fora do caminho de sondagem no contexto de carga padrão.
Load-From Contexto
O contexto load-from permite carregar um assembly de um caminho que não está sob o caminho do aplicativo e, portanto, não está incluído na sondagem. Ele permite que as dependências sejam localizadas e carregadas a partir desse caminho, porque as informações do caminho são mantidas pelo contexto. Além disso, assemblies nesse contexto podem usar dependências que são carregadas no contexto de carga padrão.
Carregar assemblies usando o Assembly.LoadFrom método, ou um dos outros métodos que carregam por caminho, tem as seguintes desvantagens:
Se um assembly com a mesma identidade já estiver carregado no contexto load-from, LoadFrom retornará o assembly carregado mesmo que um caminho diferente tenha sido especificado.
Se um assembly for carregado com LoadFrom, e posteriormente um assembly no contexto de carregamento padrão tentar carregar o mesmo assembly por nome de exibição, a tentativa de carregamento falhará. Isso pode ocorrer quando um assembly é desserializado.
Se um assembly for carregado com LoadFrom, e o caminho de sondagem incluir um assembly com a mesma identidade, mas em um local diferente, poderá ocorrer um InvalidCastException, MissingMethodExceptionou outro comportamento inesperado.
LoadFrom exige FileIOPermissionAccess.Read e FileIOPermissionAccess.PathDiscovery, ou WebPermission, no caminho especificado.
Se existir uma imagem nativa para a compilação, ela não é usada.
O conjunto não pode ser carregado como neutro ao domínio.
No .NET Framework versões 1.0 e 1.1, a política não é aplicada.
Sem contexto
O carregamento sem contexto é a única opção para assemblies transitórios que são gerados com emissão de reflexão. Carregar sem contexto é a única maneira de carregar vários assemblies que têm a mesma identidade em um domínio de aplicativo. O custo da sondagem é evitado.
Assemblagens carregadas a partir de matrizes de bytes são carregadas sem contexto, a menos que a identidade da assemblagem, que é estabelecida quando a política é aplicada, corresponda à identidade de uma assemblagem no cache de assemblagem global; nesse caso, a assemblagem é carregada a partir do cache de assemblagem global.
Carregar assemblies sem contexto tem as seguintes desvantagens:
Outros assemblies não podem vincular-se a assemblies que são carregados sem contexto, a menos que você manipule o evento AppDomain.AssemblyResolve.
As dependências não são carregadas automaticamente. Você pode pré-carregá-los sem contexto, pré-carregá-los no contexto de carga padrão ou carregá-los manipulando o AppDomain.AssemblyResolve evento.
Carregar vários assemblies com a mesma identidade sem contexto pode causar problemas de identidade de tipo semelhantes aos causados pelo carregamento de assemblies com a mesma identidade em vários contextos. Veja Evitar carregar um conjunto em vários contextos.
Se existir uma imagem nativa para a compilação, ela não é usada.
O conjunto não pode ser carregado como neutro ao domínio.
No .NET Framework versões 1.0 e 1.1, a política não é aplicada.
Evite a vinculação em nomes de assembly incompletos
A vinculação parcial de nome ocorre quando se especifica apenas parte do nome de exibição do assembly (FullName) ao carregar um assembly. Por exemplo, você pode chamar o Assembly.Load método apenas com o nome simples do assembly, omitindo a versão, a cultura e o token de chave pública. Ou você pode chamar o Assembly.LoadWithPartialName método, que primeiro chama o Assembly.Load método e, se isso não conseguir localizar o assembly, pesquisa o cache global do assembly e carrega a versão mais recente disponível do assembly.
A vinculação parcial de nomes pode causar muitos problemas, incluindo os seguintes:
O Assembly.LoadWithPartialName método pode carregar uma assembly diferente com o mesmo nome simples. Por exemplo, duas aplicações podem instalar dois assemblies completamente diferentes que têm o nome simples
GraphicsLibraryno cache de assembly global.A assembly que é realmente carregada pode não ser compatível com versões anteriores. Por exemplo, não especificar a versão pode resultar no carregamento de uma versão muito mais tardia do que a versão que o programa foi originalmente escrito para usar. Alterações na versão posterior podem causar erros em seu aplicativo.
O assembly que está realmente carregado pode não ser compatível com versões futuras. Por exemplo, você pode ter criado e testado seu aplicativo com a versão mais recente de um assembly, mas a vinculação parcial pode carregar uma versão muito anterior que não possui recursos usados pelo aplicativo.
A instalação de novos aplicativos pode quebrar aplicativos existentes. Um aplicativo que usa o LoadWithPartialName método pode ser quebrado instalando uma versão mais recente e incompatível de um assembly compartilhado.
Pode ocorrer um carregamento de dependência inesperado. Se carregares dois assemblies que partilham uma dependência, carregá-los com vinculação parcial pode resultar num dos assemblies a usar um componente com o qual não foi criado ou testado.
Devido aos problemas que pode causar, o LoadWithPartialName método foi marcado como obsoleto. Recomendamos que você use o Assembly.Load método em vez disso e especifique nomes de exibição de assembly completos. Consulte Compreender as vantagens e desvantagens dos contextos de carga e considere mudar para o contexto de carga padrão.
Se você quiser usar o método porque ele facilita o LoadWithPartialName carregamento do assembly, considere que ter seu aplicativo falhando com uma mensagem de erro que identifica o assembly ausente provavelmente fornecerá uma experiência de usuário melhor do que usar automaticamente uma versão desconhecida do assembly, o que pode causar comportamento imprevisível e falhas de segurança.
Evite carregar um assembly em vários contextos
Carregar um assembly em vários contextos pode causar problemas de identidade de tipos. Se o mesmo tipo é carregado do mesmo assembly em dois contextos diferentes, é como se dois tipos diferentes com o mesmo nome tivessem sido carregados. Um InvalidCastException é lançado se tentares converter um tipo para outro, com a mensagem confusa de que o tipo MyType não pode ser convertido para o tipo MyType.
Por exemplo, suponha que a interface ICommunicate seja declarada numa assembly chamada Utility, que é referenciada pelo seu programa e também por outras assemblies que o programa carrega. Esses outros assemblies contêm tipos que implementam a ICommunicate interface, permitindo que seu programa os use.
Agora considere o que acontece quando o seu programa é executado. As assemblagens que são referenciadas pelo seu programa são carregadas no contexto de carregamento padrão. Se carregar um assembly de destino por sua identidade usando o método Load, ele estará no contexto de carregamento padrão, assim como suas dependências. Tanto o seu programa quanto o assembly de destino irão usar o mesmo Utility assembly.
No entanto, suponha que você carregue o assembly de destino por seu caminho de arquivo, usando o LoadFile método. O assembly é carregado sem qualquer contexto, portanto, suas dependências não são carregadas automaticamente. Você pode ter um manipulador para o evento AppDomain.AssemblyResolve para fornecer a dependência, e ele pode carregar a assemblagem Utility sem contexto usando o método LoadFile. Agora, quando você cria uma instância de um tipo que está contido no assembly de destino e tenta atribuí-la a uma variável do tipo ICommunicate, um InvalidCastException é lançado porque o tempo de execução considera as ICommunicate interfaces nas duas cópias do Utility assembly como tipos diferentes.
Há muitos outros cenários em que um assembly pode ser carregado em múltiplos contextos. A melhor abordagem é evitar conflitos realocando o assembly de destino no caminho da aplicação e usando o método Load com o nome de exibição completo. A assemblagem é então carregada no contexto de carga padrão e ambas as assemblagens usam a mesma Utility assemblagem.
Se o assembly de destino deve permanecer fora do caminho do aplicativo, você pode usar o LoadFrom método para carregá-lo no contexto load-from. Se o assembly de destino foi compilado com uma referência à assembly do seu aplicativo Utility, ele usará a assembly Utility que o seu aplicativo carregou no contexto de carregamento padrão. Tenha em atenção que podem ocorrer problemas se o assembly de destino tiver uma dependência de uma cópia do assembly do Utility localizada fora do caminho da aplicação. Se essa assembly for carregada no contexto de carregamento antes que a sua aplicação carregue a Utility assembly, a carga da sua aplicação falhará.
A seção Considere alternar para o contexto de carga padrão discute alternativas ao uso de carregamentos de caminho de arquivo, como LoadFile e LoadFrom.
Evite carregar várias versões de um assembly no mesmo contexto
Carregar várias versões de um conjunto no contexto de carregamento pode causar problemas de identidade de tipo. Se o mesmo tipo é carregado a partir de duas versões do mesmo assembly, é como se dois tipos diferentes com o mesmo nome tivessem sido carregados. Um InvalidCastException é lançado se tentares converter um tipo para outro, com a mensagem confusa de que o tipo MyType não pode ser convertido para o tipo MyType.
Por exemplo, seu programa pode carregar uma versão do Utility assembly diretamente e, mais tarde, ele pode carregar outro assembly que carrega uma versão diferente do Utility assembly. Ou um erro de codificação pode fazer com que dois caminhos de código diferentes em seu aplicativo carreguem versões diferentes de um assembly.
No contexto de carregamento padrão, esse problema pode ocorrer quando você usa o Assembly.Load método e especificar nomes de exibição de assembly completo que incluem números de versão diferentes. Para assemblies que são carregados sem contexto, o problema pode ser causado ao usar o método Assembly.LoadFile para carregar o mesmo assembly a partir de caminhos diferentes. O ambiente de execução considera dois conjuntos de assembly que são carregados de caminhos diferentes como conjuntos de assembly diferentes, mesmo que suas identidades sejam iguais.
Além de problemas de identidade de tipo, várias versões de um assembly podem causar um MissingMethodException se um tipo carregado a partir de uma versão do assembly for passado para o código que requer esse tipo a partir de uma versão diferente. Por exemplo, o código pode esperar um método que foi adicionado à versão posterior.
Erros mais sutis podem ocorrer se o comportamento do tipo mudou entre as versões. Por exemplo, um método pode lançar uma exceção inesperada ou retornar um valor inesperado.
Analise cuidadosamente seu código para garantir que apenas uma versão de um assembly seja carregada. Pode usar o método AppDomain.GetAssemblies para determinar quais assemblies estão carregados a qualquer momento.
Considere mudar para o contexto de carregamento padrão
Examine os padrões de montagem e implantação do seu aplicativo. Você pode eliminar assemblies que são carregados de matrizes de bytes? É possível mover componentes para o caminho de sondagem? Se os assemblies estiverem localizados no cache global de assemblies ou no caminho de pesquisa do domínio do aplicativo (ou seja, seu ApplicationBase e PrivateBinPath), você poderá carregar o assembly por sua identidade.
Se não for possível colocar todos os assemblies no caminho de sondagem, considere alternativas como usar o modelo de extensão do .NET Framework, colocar assemblies no cache global ou criar domínios de aplicação.
Considere usar o modelo de Add-In do .NET Framework
Se você estiver usando o contexto de carregamento para implementar complementos, que normalmente não são instalados na base do aplicativo, use o modelo de complementos do .NET Framework. Esse modelo fornece isolamento no nível do domínio do aplicativo ou do processo, sem exigir que você gerencie os domínios do aplicativo por conta própria. Para obter informações sobre o modelo de suplemento, consulte Suplementos e extensibilidade.
Considere o uso do Global Assembly Cache
Coloque assemblies no cache de assembly global para obter o benefício de um caminho de assembly compartilhado que está fora da base do aplicativo, sem perder as vantagens do contexto de carga padrão ou assumir as desvantagens dos outros contextos.
Considere o uso de domínios de aplicativo
Se você determinar que alguns de seus assemblies não podem ser implantados no caminho de sondagem do aplicativo, considere a criação de um novo domínio de aplicativo para esses assemblies. Use um AppDomainSetup para criar o novo domínio de aplicativo e use a AppDomainSetup.ApplicationBase propriedade para especificar o caminho que contém os assemblies que você deseja carregar. Se você tiver vários diretórios para sondar, poderá definir o ApplicationBase como um diretório raiz e usar a AppDomainSetup.PrivateBinPath propriedade para identificar os subdiretórios a serem investigados. Como alternativa, poderá criar vários domínios de aplicação e definir o ApplicationBase de cada domínio de aplicação para o caminho apropriado para os seus assemblies.
Observe que você pode usar o método Assembly.LoadFrom para carregar esses assemblies. Como eles agora estão no caminho de sondagem, eles serão carregados no contexto de carga padrão em vez do contexto load-from. No entanto, recomendamos que você alterne para o Assembly.Load método e forneça nomes de exibição de assembly completos para garantir que as versões corretas sejam sempre usadas.