Procedimiento Inspección del contenido de un ensamblado con MetadataLoadContext

De forma predeterminada, la API de reflexión de .NET permite a los desarrolladores inspeccionar el contenido de los ensamblados cargados en el contexto de ejecución principal. Sin embargo, a veces no es posible cargar un ensamblado en el contexto de ejecución, por ejemplo, porque se compiló para otra arquitectura de procesador o plataforma, o es un ensamblado de referencia. La API de System.Reflection.MetadataLoadContext permite cargar e inspeccionar dichos ensamblados. Los ensamblados cargados en MetadataLoadContext se tratan únicamente como metadatos, es decir, se pueden examinar los tipos del ensamblado, pero no se puede ejecutar ningún código que contenga. A diferencia del contexto de ejecución principal, MetadataLoadContext no carga automáticamente las dependencias del directorio actual, sino que usa la lógica de enlace personalizada proporcionada por la clase MetadataAssemblyResolver que se le ha pasado.

Requisitos previos

Para usar MetadataLoadContext, instale el paquete NuGet System.Reflection.MetadataLoadContext. Se admite en cualquier plataforma de destino compatible con .NET Standard 2.0, por ejemplo, .NET Core 2.0 o .NET Framework 4.6.1.

Creación de MetadataAssemblyResolver para MetadataLoadContext

Para crear la clase MetadataLoadContext es necesario proporcionar la instancia de la clase MetadataAssemblyResolver. La manera más sencilla de proporcionar una es usar la clase PathAssemblyResolver, que resuelve los ensamblados de la colección especificada de cadenas de ruta de acceso de ensamblado. Esta colección, además de los ensamblados que quiere inspeccionar directamente, también debe incluir todas las dependencias necesarias. Por ejemplo, para leer el atributo personalizado que se encuentra en un ensamblado externo, debe incluir ese ensamblado o se producirá una excepción. En la mayoría de los casos, debe incluir al menos el ensamblado principal, es decir, el ensamblado que contiene tipos de sistema integrados, como System.Object. En el código siguiente se muestra cómo crear la clase PathAssemblyResolver mediante la colección que consta del ensamblado inspeccionado y el ensamblado principal del entorno de ejecución actual:

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

Si necesita tener acceso a todos los tipos de BCL, puede incluir todos los ensamblados en tiempo de ejecución de la colección. En el código siguiente se muestra cómo crear la clase PathAssemblyResolver mediante la colección que consta del ensamblado inspeccionado y todos los ensamblados del entorno de ejecución actual:

// 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);

Creación de MetadataLoadContext

Para crear la clase MetadataLoadContext, invoque su constructor MetadataLoadContext(MetadataAssemblyResolver, String), y pase la clase MetadataAssemblyResolver creada previamente como primer parámetro y el nombre del ensamblado principal como segundo parámetro. Puede omitir el nombre del ensamblado principal, en cuyo caso el constructor intentará usar nombres predeterminados: "mscorlib", "System.Runtime" o "netstandard".

Después de crear el contexto, puede cargar los ensamblados en él mediante métodos como LoadFromAssemblyPath. Puede usar todas las API de reflexión en los ensamblados cargados, excepto las que tienen que ver con la ejecución de código. El método GetCustomAttributes supone la ejecución de constructores, así que use el método GetCustomAttributesData en su lugar cuando necesite examinar los atributos personalizados de la clase MetadataLoadContext.

En el siguiente ejemplo de código se crea la clase MetadataLoadContext, se carga en ella el ensamblado y se generan los atributos del ensamblado en la consola:

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}");
        }
    }
}

Si necesita probar tipos en MetadataLoadContext para determinar si son iguales o asignables, use solo los objetos de tipo cargados en ese contexto. No se admite la mezcla de tipos MetadataLoadContext con tipos en tiempo de ejecución. Por ejemplo, considere un tipo testedType en MetadataLoadContext. Si necesita probar si otro tipo es asignable desde él, no use código como typeof(MyType).IsAssignableFrom(testedType). En su lugar, use 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)}");
}

Ejemplo

Para obtener un ejemplo de código completo, vea el ejemplo Inspección del contenido de un ensamblado mediante MetadataLoadContext.

Consulte también