Partilhar via


Como: Inspecionar o conteúdo do assembly usando MetadataLoadContext

A API de reflexão no .NET por padrão permite que os desenvolvedores inspecionem o conteúdo dos assemblies carregados no contexto de execução principal. No entanto, às vezes não é possível carregar um assembly no contexto de execução, por exemplo, porque ele foi compilado para outra plataforma ou arquitetura de processador, ou é um assembly de referência. A System.Reflection.MetadataLoadContext API permite que você carregue e inspecione esses assemblies. Os assemblies carregados no MetadataLoadContext são tratados apenas como metadados, ou seja, você pode examinar tipos no assembly, mas não pode executar nenhum código contido nele. Ao contrário do contexto de execução principal, o MetadataLoadContext não carrega automaticamente dependências do diretório atual, em vez disso, usa a lógica de vinculação personalizada fornecida pelo MetadataAssemblyResolver passado para ele.

Pré-requisitos

Para usar MetadataLoadContexto , instale o pacote NuGet System.Reflection.MetadataLoadContext . Ele é suportado em qualquer estrutura de destino compatível com .NET Standard 2.0, por exemplo, .NET Core 2.0 ou .NET Framework 4.6.1.

Criar MetadataAssemblyResolver para MetadataLoadContext

A criação do MetadataLoadContext requer o fornecimento da instância do MetadataAssemblyResolver. A maneira mais simples de fornecer um é usar o PathAssemblyResolver, que resolve assemblies a partir de uma determinada coleção de cadeias de caracteres de caminho de assembly. Essa coleção, além dos assemblies que você deseja inspecionar diretamente, também deve incluir todas as dependências necessárias. Por exemplo, para ler o atributo personalizado localizado em um assembly externo, você deve incluir esse assembly ou uma exceção será lançada. Na maioria dos casos, você deve incluir pelo menos o assembly principal, ou seja, o assembly que contém tipos de sistema internos, como System.Object. O código a seguir mostra como criar o PathAssemblyResolver usando a coleção que consiste no assembly inspecionado e no assembly principal do tempo de execução atual:

var resolver = new PathAssemblyResolver(new string[] { "ExampleAssembly.dll", typeof(object).Assembly.Location });

Se precisar de acesso a todos os tipos de BCL, você pode incluir todos os assemblies de tempo de execução na coleção. O código a seguir mostra como criar o PathAssemblyResolver usando a coleção que consiste no assembly inspecionado e todos os assemblies do tempo de execução atual:

// Get the array of runtime assemblies.
string[] runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll");

// Create the list of assembly paths consisting of runtime assemblies and the inspected assembly.
var paths = new List<string>(runtimeAssemblies);
paths.Add("ExampleAssembly.dll");

// Create PathAssemblyResolver that can resolve assemblies using the created list.
var resolver = new PathAssemblyResolver(paths);

Criar MetadataLoadContext

Para criar o MetadataLoadContext, invoque seu construtor MetadataLoadContext(MetadataAssemblyResolver, String), passando o anteriormente criado MetadataAssemblyResolver como o primeiro parâmetro e o nome do assembly principal como o segundo parâmetro. Você pode omitir o nome do assembly principal, caso em que o construtor tentará usar nomes padrão: "mscorlib", "System.Runtime" ou "netstandard".

Depois de criar o contexto, você pode carregar assemblies nele usando métodos como LoadFromAssemblyPath. Você pode usar todas as APIs de reflexão em assemblies carregados, exceto aqueles que envolvem a execução de código. O GetCustomAttributes método envolve a execução de construtores, portanto, use o GetCustomAttributesData método em vez disso quando precisar examinar atributos personalizados no MetadataLoadContext.

O exemplo de código a seguir cria MetadataLoadContext, carrega o assembly nele e gera atributos de assembly no console:

var mlc = new MetadataLoadContext(resolver);

using (mlc)
{
    // Load assembly into MetadataLoadContext.
    Assembly assembly = mlc.LoadFromAssemblyPath("ExampleAssembly.dll");
    AssemblyName name = assembly.GetName();

    // Print assembly attribute information.
    Console.WriteLine($"{name.Name} has following attributes: ");

    foreach (CustomAttributeData attr in assembly.GetCustomAttributesData())
    {
        try
        {
            Console.WriteLine(attr.AttributeType);
        }
        catch (FileNotFoundException ex)
        {
            // We are missing the required dependency assembly.
            Console.WriteLine($"Error while getting attribute type: {ex.Message}");
        }
    }
}

Se você precisar testar tipos para MetadataLoadContext igualdade ou atribuibilidade, use apenas objetos de tipo carregados nesse contexto. Não há suporte para a mistura de MetadataLoadContext tipos com tipos de tempo de execução. Por exemplo, considere um tipo testedType em MetadataLoadContext. Se você precisar testar se outro tipo é atribuível a partir dele, não use código como typeof(MyType).IsAssignableFrom(testedType). Em vez disso, use um código como este:

Assembly matchAssembly = mlc.LoadFromAssemblyPath(typeof(MyType).Assembly.Location);
Type matchType = assembly.GetType(typeof(MyType).FullName!)!;

if (matchType.IsAssignableFrom(testedType))
{
    Console.WriteLine($"{nameof(matchType)} is assignable from {nameof(testedType)}");
}

Exemplo

Para obter um exemplo de código completo, consulte o Inspecionar o conteúdo do assembly usando o exemplo MetadataLoadContext.

Consulte também