Практическое руководство. Исследование содержимого сборки с помощью MetadataLoadContext
API отражения в .NET по умолчанию позволяет разработчикам исследовать содержимое сборок, загруженных в основной контекст выполнения (т.е., программно получать входящие в сборку типы и выполнять какие-то действия на основе их метаданных). Однако, иногда невозможно загрузить сборку в контекст выполнения, например, если она была скомпилирована для другой платформы или архитектуры процессора, либо если это ссылочная сборка. API System.Reflection.MetadataLoadContext позволяет загружать и исследовать такие сборки. Сборки, загруженные в MetadataLoadContext, обрабатываются только как метаданные, то есть вы можете получать информацию о типах в сборке, но не выполнять код, содержащийся в ней. В отличие от основного контекста выполнения, MetadataLoadContext не загружает зависимости из текущего каталога автоматически. Вместо этого используется пользовательская логика привязки, предоставляемая передаваемым в него объектом MetadataAssemblyResolver.
Предварительные требования
Чтобы использовать MetadataLoadContext, установите пакет NuGet System.Reflection.MetadataLoadContext. Он поддерживается на любой целевой платформе, совместимой с .NET Standard 2.0, например .NET Core 2.0 или .NET Framework 4.6.1.
Создание MetadataAssemblyResolver для MetadataLoadContext
Создание MetadataLoadContext требует предоставления экземпляра MetadataAssemblyResolver. Проще всего использовать класс PathAssemblyResolver, который разрешает сборки из заданной коллекции путей к файлам сборок. Эта коллекция, помимо сборок, с которыми вы непосредственно хотите работать, также должна включать все необходимые зависимости. Например, для чтения атрибута, расположенного во внешней сборке, следует включить эту сборку, иначе будет вызвано исключение. В большинстве случаев следует включать по крайней мере основную сборку, то есть сборку, содержащую встроенные системные типы, такие как System.Object. В следующем коде показано, как создать PathAssemblyResolver с помощью коллекции, состоящей из исследуемой сборки и основной сборки текущей среды выполнения:
var resolver = new PathAssemblyResolver(new string[] { "ExampleAssembly.dll", typeof(object).Assembly.Location });
Если требуется доступ ко всем типам BCL, в коллекцию можно включить все сборки среды выполнения. В следующем коде показано, как создать PathAssemblyResolver с помощью коллекции, состоящей из исследуемой сборки и всех сборок текущей среды выполнения:
// 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);
Создание MetadataLoadContext
Чтобы создать MetadataLoadContext, вызовите его конструктор MetadataLoadContext(MetadataAssemblyResolver, String), передав ранее созданный MetadataAssemblyResolver в качестве первого параметра и имя основной сборки в качестве второго параметра. Имя основной сборки можно опустить, в этом случае конструктор попытается использовать имена по умолчанию: "mscorlib", "System.Runtime" или "netstandard".
После создания контекста в него можно загрузить сборки с помощью таких методов, как LoadFromAssemblyPath. В загруженных сборках можно использовать все API отражения, за исключением тех, которые требуют выполнение кода. Напрмиер, метод GetCustomAttributes требует выполнения конструкторов, поэтому, если необходимо исследовать пользовательские атрибуты в MetadataLoadContext, вместо этого следует использовать метод GetCustomAttributesData.
Следующий пример кода создает MetadataLoadContext, загружает в него сборку и выводит атрибуты сборки в консоль:
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}");
}
}
}
Пример
Полный пример кода см. на странице Проверка содержимого сборки с помощью MetadataLoadContext.