由于缺少程序集(.NET 核心),通过 MetadataLoadContext 检查类型失败
Inspect Types via MetadataLoadContext fails due to missing assembly (.NET core)
我想检查程序集是否具有特定类型,而无需在当前范围内加载程序集,这可通过 .NET Core 3 中的 MetadataLoadContext 获得。
但是如果我尝试下面的例子
internal static class Program
{
// ReSharper disable once UnusedParameter.Local
private static void Main(string[] args)
{
var paths = new string[] { @"Plugin.dll" };
var resolver = new PathAssemblyResolver(paths);
var pluginInterface = typeof(IPlugin);
using (var context = new MetadataLoadContext(resolver))
{
var assembly =
context.LoadFromAssemblyName(@"Plugin");
foreach (var type in assembly.GetTypes())
{
if (type.IsClass && pluginInterface.IsAssignableFrom(type))
Console.WriteLine("found");
}
}
}
}
我遇到异常
System.IO.FileNotFoundException: Could not find core assembly. Either specify a valid core assembly name in the MetadataLoadContext constructor or provide a MetadataAssemblyResolver that can load the core assembly.
在 var context = new MetadataLoadContext(resolver)
核心组件是什么意思?或者我做错了什么?
https://blog.vincentbitter.nl/net-core-3-0/ 似乎对我不起作用。
提供 .NET 核心库的以下路径有效
var paths = new string[] {@"Plugin.dll", @"netstandard.dll", "System.Runtime.dll"};
现有答案对我不起作用(.NET Core 2.1)。它失败并出现 System.Runtime 未找到的错误。如果我硬编码 System.Runtime 的完整路径,它对其他程序集失败,例如 System.Private.CoreLib。当一种类型不是来自 MetadataLoadContext 时,通过 IsAssignableFrom 检查类型似乎也不起作用。
程序集加载错误的可能解决方案是包括所有 BCL 程序集(RuntimeEnvironment.GetRuntimeDirectory 返回的目录中的所有 .dll 文件)。这感觉有些愚蠢,因为并非所有这些实际上都是托管程序集,但它似乎有效。下面是搜索通过 MetadataLoadContext 实现接口的类型的完整示例:
using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
namespace MetadataLoadContextSample
{
class Program
{
static int Main(string[] args)
{
string inputFile = @"Plugin.dll";
string[] runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll");
var paths = new List<string>(runtimeAssemblies);
paths.Add(inputFile);
var resolver = new PathAssemblyResolver(paths);
var context = new MetadataLoadContext(resolver);
using (context)
{
Assembly assembly = context.LoadFromAssemblyPath(inputFile);
AssemblyName name = assembly.GetName();
foreach (TypeInfo t in assembly.GetTypes())
{
try
{
if (t.IsClass && t.GetInterface("IPlugin") != null)
{
Console.WriteLine(t.Name);
}
}
catch (FileNotFoundException ex)
{
Console.WriteLine("FileNotFoundException: " + ex.Message);
}
catch (TypeLoadException ex)
{
Console.WriteLine("TypeLoadException: " + ex.Message);
}
}
}
Console.ReadLine();
return 0;
}
}
}
这是一种适用于 net6.0
的方法。该方法从作为参数提供的程序集中提取自定义属性。
public static FreshInfo GetInfo(string pathToMainExecutable) {
if (string.IsNullOrWhiteSpace(pathToMainExecutable))
throw new ArgumentException(@"Value cannot be null or whitespace.", nameof(pathToMainExecutable));
if (File.Exists(pathToMainExecutable) == false)
throw new FileNotFoundException($"File [{pathToMainExecutable}] does not exists on disk.");
var runtimeDirectory = RuntimeEnvironment.GetRuntimeDirectory();
var pathToSystemRuntime = Path.Combine(runtimeDirectory, "System.Runtime.dll");
if (File.Exists(pathToSystemRuntime) == false)
throw new FileNotFoundException($"Could not find [{pathToSystemRuntime}].");
var pathToSystemPrivateCoreLib = Path.Combine(runtimeDirectory, "System.Private.CoreLib.dll");
if (File.Exists(pathToSystemPrivateCoreLib) == false)
throw new FileNotFoundException($"Could not find [{pathToSystemPrivateCoreLib}].");
// make sure that we are referring to the .net dll/assembly but not the exe bootstrapper
// exe file in net6.0/core is not a managed file it's a native executable
// TODO do not assume that the exe file has the same name as the dll file
// managed dll filename can be extracted from a VERSIONINFO Resource in the native exe
pathToMainExecutable = pathToMainExecutable.ReplaceEnd("exe", "dll");
var assemblyNames = new List<string> {
Path.GetFileName(pathToMainExecutable)
, "Fresh.Updater.dll"
, pathToSystemRuntime
, pathToSystemPrivateCoreLib
};
var metadataAssemblyResolver = new PathAssemblyResolver(assemblyNames);
using (var mlc = new MetadataLoadContext(metadataAssemblyResolver)) {
var mainAssembly = mlc.LoadFromAssemblyPath(pathToMainExecutable);
var buildTime = ExtractBuildTime(mainAssembly);
var appId = ExtractAppId(mainAssembly);
var appFolder = Path.GetDirectoryName(pathToMainExecutable);
return new FreshInfo(appId, buildTime) {
AppFolder = appFolder
};
}
}
我想检查程序集是否具有特定类型,而无需在当前范围内加载程序集,这可通过 .NET Core 3 中的 MetadataLoadContext 获得。
但是如果我尝试下面的例子
internal static class Program
{
// ReSharper disable once UnusedParameter.Local
private static void Main(string[] args)
{
var paths = new string[] { @"Plugin.dll" };
var resolver = new PathAssemblyResolver(paths);
var pluginInterface = typeof(IPlugin);
using (var context = new MetadataLoadContext(resolver))
{
var assembly =
context.LoadFromAssemblyName(@"Plugin");
foreach (var type in assembly.GetTypes())
{
if (type.IsClass && pluginInterface.IsAssignableFrom(type))
Console.WriteLine("found");
}
}
}
}
我遇到异常
System.IO.FileNotFoundException: Could not find core assembly. Either specify a valid core assembly name in the MetadataLoadContext constructor or provide a MetadataAssemblyResolver that can load the core assembly.
在 var context = new MetadataLoadContext(resolver)
核心组件是什么意思?或者我做错了什么? https://blog.vincentbitter.nl/net-core-3-0/ 似乎对我不起作用。
提供 .NET 核心库的以下路径有效
var paths = new string[] {@"Plugin.dll", @"netstandard.dll", "System.Runtime.dll"};
现有答案对我不起作用(.NET Core 2.1)。它失败并出现 System.Runtime 未找到的错误。如果我硬编码 System.Runtime 的完整路径,它对其他程序集失败,例如 System.Private.CoreLib。当一种类型不是来自 MetadataLoadContext 时,通过 IsAssignableFrom 检查类型似乎也不起作用。
程序集加载错误的可能解决方案是包括所有 BCL 程序集(RuntimeEnvironment.GetRuntimeDirectory 返回的目录中的所有 .dll 文件)。这感觉有些愚蠢,因为并非所有这些实际上都是托管程序集,但它似乎有效。下面是搜索通过 MetadataLoadContext 实现接口的类型的完整示例:
using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
namespace MetadataLoadContextSample
{
class Program
{
static int Main(string[] args)
{
string inputFile = @"Plugin.dll";
string[] runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll");
var paths = new List<string>(runtimeAssemblies);
paths.Add(inputFile);
var resolver = new PathAssemblyResolver(paths);
var context = new MetadataLoadContext(resolver);
using (context)
{
Assembly assembly = context.LoadFromAssemblyPath(inputFile);
AssemblyName name = assembly.GetName();
foreach (TypeInfo t in assembly.GetTypes())
{
try
{
if (t.IsClass && t.GetInterface("IPlugin") != null)
{
Console.WriteLine(t.Name);
}
}
catch (FileNotFoundException ex)
{
Console.WriteLine("FileNotFoundException: " + ex.Message);
}
catch (TypeLoadException ex)
{
Console.WriteLine("TypeLoadException: " + ex.Message);
}
}
}
Console.ReadLine();
return 0;
}
}
}
这是一种适用于 net6.0
的方法。该方法从作为参数提供的程序集中提取自定义属性。
public static FreshInfo GetInfo(string pathToMainExecutable) {
if (string.IsNullOrWhiteSpace(pathToMainExecutable))
throw new ArgumentException(@"Value cannot be null or whitespace.", nameof(pathToMainExecutable));
if (File.Exists(pathToMainExecutable) == false)
throw new FileNotFoundException($"File [{pathToMainExecutable}] does not exists on disk.");
var runtimeDirectory = RuntimeEnvironment.GetRuntimeDirectory();
var pathToSystemRuntime = Path.Combine(runtimeDirectory, "System.Runtime.dll");
if (File.Exists(pathToSystemRuntime) == false)
throw new FileNotFoundException($"Could not find [{pathToSystemRuntime}].");
var pathToSystemPrivateCoreLib = Path.Combine(runtimeDirectory, "System.Private.CoreLib.dll");
if (File.Exists(pathToSystemPrivateCoreLib) == false)
throw new FileNotFoundException($"Could not find [{pathToSystemPrivateCoreLib}].");
// make sure that we are referring to the .net dll/assembly but not the exe bootstrapper
// exe file in net6.0/core is not a managed file it's a native executable
// TODO do not assume that the exe file has the same name as the dll file
// managed dll filename can be extracted from a VERSIONINFO Resource in the native exe
pathToMainExecutable = pathToMainExecutable.ReplaceEnd("exe", "dll");
var assemblyNames = new List<string> {
Path.GetFileName(pathToMainExecutable)
, "Fresh.Updater.dll"
, pathToSystemRuntime
, pathToSystemPrivateCoreLib
};
var metadataAssemblyResolver = new PathAssemblyResolver(assemblyNames);
using (var mlc = new MetadataLoadContext(metadataAssemblyResolver)) {
var mainAssembly = mlc.LoadFromAssemblyPath(pathToMainExecutable);
var buildTime = ExtractBuildTime(mainAssembly);
var appId = ExtractAppId(mainAssembly);
var appFolder = Path.GetDirectoryName(pathToMainExecutable);
return new FreshInfo(appId, buildTime) {
AppFolder = appFolder
};
}
}