Vorgehensweise: Untersuchen von Assemblyinhalten mithilfe von MetadataLoadContext

Mithilfe der Reflektions-API in .NET können Entwickler standardmäßig die Inhalte von Assemblys untersuchen, die im Hauptausführungskontext geladen werden. Manchmal ist es jedoch nicht möglich, eine Assembly in den Ausführungskontext zu laden – weil sie beispielsweise für eine andere Plattform oder Prozessorarchitektur kompiliert wurde, oder weil es sich um eine Referenzassembly handelt. Die System.Reflection.MetadataLoadContext-API ermöglicht es Ihnen, solche Assemblys zu laden und zu untersuchen. In den MetadataLoadContext geladene Assemblys werden lediglich als Metadaten behandelt, d. h. Sie können Typen in der Assembly untersuchen, aber keinen darin enthaltenen Code ausführen. Im Gegensatz zum Hauptausführungskontext lädt der MetadataLoadContext nicht automatisch Abhängigkeiten aus dem aktuellen Verzeichnis. Stattdessen wird die benutzerdefinierte Bindungslogik aus MetadataAssemblyResolver verwendet, der an sie übergeben wird.

Voraussetzungen

Installieren Sie zur Verwendung von MetadataLoadContext das NuGet-Paket System.Reflection.MetadataLoadContext. Es wird in jedem .NET Standard 2.0-konformen Zielframework unterstützt, wie etwa .NET Core 2.0 oder .NET Framework 4.6.1.

Erstellen von „MetadataAssemblyResolver“ für MetadataLoadContext

Zum Erstellen des MetadataLoadContext muss eine MetadataAssemblyResolver-Instanz bereitgestellt werden. Die einfachste Möglichkeit hierzu ist die Verwendung von PathAssemblyResolver, der Assemblys aus der angegebenen Sammlung mit Assembly-Pfadzeichenfolgen auflöst. Diese Sammlung sollte neben den Assemblys, die Sie direkt untersuchen möchten, auch alle benötigten Abhängigkeiten umfassen. Um beispielsweise das custom-Attribut in einer externen Assembly zu lesen, müssen Sie diese Assembly einschließen, da ansonsten eine Ausnahme ausgelöst wird. In den meisten Fällen sollten Sie mindestens die Kernassembly einschließen – also die Assembly, die integrierte Systemtypen wie z. B. System.Object enthält. Der folgende Code zeigt, wie Sie den PathAssemblyResolver mithilfe der Sammlung erstellen, die die untersuchte Assembly und die Kernassembly der aktuellen Runtime enthält:

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

Wenn Sie Zugriff auf alle BCL-Typen benötigen, können Sie alle Runtimeassemblys in die Sammlung aufnehmen. Der folgende Code zeigt, wie Sie den PathAssemblyResolver mithilfe der Sammlung erstellen, die die untersuchte Assembly und alle Assemblys der aktuellen Runtime enthält:

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

Erstellen von MetadataLoadContext

Um den MetadataLoadContext zu erstellen, rufen Sie den zugehörigen Konstruktor MetadataLoadContext(MetadataAssemblyResolver, String) auf und übergeben den zuvor erstellten MetadataAssemblyResolver als ersten Parameter und den Namen der Kernassembly als zweiten Parameter. Sie können den Namen der Kernassembly weglassen. In diesem Fall versucht der Konstruktor, Standardnamen wie „mscorlib“, „System.Runtime“ oder „netstandard“ zu verwenden.

Nachdem Sie den Kontext erstellt haben, können Sie mithilfe von Methoden wie z. B. LoadFromAssemblyPath Assemblys in den Kontext laden. Sie können für geladene Assemblys alle Reflektions-APIs verwenden – mit Ausnahme derjenigen, die eine Codeausführung umfassen. Die GetCustomAttributes-Methode hingegen schließt eine Ausführung von Konstruktoren ein. Verwenden Sie deshalb stattdessen die GetCustomAttributesData-Methode, wenn Sie benutzerdefinierte Attribute im MetadataLoadContext untersuchen müssen.

Das folgende Codebeispiel erstellt MetadataLoadContext, lädt die Assembly in den Kontext und gibt Assemblyattribute in der Konsole aus:

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

Wenn Sie Typen in MetadataLoadContext auf Gleichheit oder Zuordnung testen müssen, verwenden Sie nur Typobjekte, die in diesen Kontext geladen werden. Das Mischen von MetadataLoadContext-Typen mit Runtimetypen wird nicht unterstützt. Ziehen Sie beispielsweise einen testedType-Typ in MetadataLoadContext in Betracht: Wenn Sie testen müssen, ob ein anderer Typ daraus zugewiesen werden kann, verwenden Sie keinen Code wie typeof(MyType).IsAssignableFrom(testedType). Verwenden Sie stattdessen einen solchen Code:

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

Beispiel

Ein vollständiges Codebeispiel finden Sie unter Inspect assembly contents using MetadataLoadContext (Untersuchen von Assemblyinhalten mithilfe von MetadataLoadContext).

Weitere Informationen