AssemblyDependencyResolver通过解析.deps.json和.runtimeconfig.json文件,为.NET Core应用提供可预测的程序集加载机制。它依据.deps.json中的依赖映射和探测路径,精准定位DLL,避免版本冲突,解决“DLL Hell”问题。结合AssemblyLoadContext,可实现插件隔离,支持自定义加载策略,确保各组件依赖独立,提升应用可维护性与扩展性。

.NET的
AssemblyDependencyResolver
.deps.json
.runtimeconfig.json
在我看来,
AssemblyDependencyResolver
.deps.json
当运行时需要加载一个特定的程序集时,
AssemblyDependencyResolver
.deps.json
.runtimeconfig.json
Microsoft.NETCore.App
更进一步,
AssemblyDependencyResolver
.deps.json
AssemblyDependencyResolver
说实话,在
AssemblyDependencyResolver
AppDomain
AppDomain
在.NET Core的设计哲学里,一切都围绕着包(NuGet包)和部署的灵活性。传统的GAC概念被抛弃了,这意味着每个应用程序都可能带着自己的一套依赖。如果没有一个清晰、统一的机制来管理这些依赖,我们很快就会回到“DLL Hell”的泥潭。
AssemblyDependencyResolver
首先,可预测性:通过强制所有依赖信息都写入
.deps.json
其次,隔离性:这一点在构建插件化应用时尤为关键。借助
AssemblyLoadContext
AssemblyDependencyResolver
要真正理解
AssemblyDependencyResolver
.deps.json
.deps.json
targets
.NETCoreApp,Version=vX.Y
libraries
runtimeTarget
当
AssemblyDependencyResolver
AssemblyLoadContext
.deps.json
Newtonsoft.Json
.deps.json
Newtonsoft.Json.dll
newtonsoft.json/13.0.1/lib/net6.0/Newtonsoft.Json.dll
有了这份“地图”,
AssemblyDependencyResolver
.deps.json
AssemblyDependencyResolver
.deps.json
.deps.json
Newtonsoft.Json.dll
./Newtonsoft.Json/13.0.1/lib/net6.0/
AssemblyDependencyResolver
.runtimeconfig.json
Microsoft.NETCore.App
当
AssemblyLoadContext
AssemblyDependencyResolver
ResolveAssemblyToPath
ResolveUnmanagedDllToPath
AssemblyDependencyResolver
.deps.json
AssemblyLoadContext
虽然
AssemblyDependencyResolver
AssemblyLoadContext
最常见的定制场景就是构建一个隔离的插件加载机制。设想你有一个主应用程序,它需要动态加载多个第三方插件,而这些插件可能各自依赖不同版本的同一个库。这时,你不能让所有插件都共享默认的
AssemblyLoadContext
AssemblyDependencyResolver
解决方案是为每个插件创建一个独立的AssemblyLoadContext
AssemblyLoadContext
AssemblyDependencyResolver
AssemblyDependencyResolver
.deps.json
下面是一个简化的代码示例,展示了如何为一个插件创建独立的加载上下文:
using System.Reflection;
using System.Runtime.Loader; // 注意这个命名空间
using System.IO;
public class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public PluginLoadContext(string pluginPath) : base(isCollectible: true) // isCollectible: true 允许卸载
{
// 假设插件的.deps.json和.runtimeconfig.json在pluginPath下
// 并且它们的名称与插件主程序集的文件名一致
var depsJsonPath = Path.Combine(pluginPath, $"{Path.GetFileName(pluginPath)}.deps.json");
// 检查文件是否存在,防止运行时错误
if (!File.Exists(depsJsonPath))
{
throw new FileNotFoundException($"Plugin .deps.json not found at: {depsJsonPath}");
}
// 为当前插件的上下文创建一个专属的AssemblyDependencyResolver
_resolver = new AssemblyDependencyResolver(depsJsonPath);
// 订阅Resolving事件,用于处理无法通过_resolver找到的程序集
// 这通常用于加载共享框架程序集,或处理主应用与插件共享的公共库
this.Resolving += OnResolving;
this.ResolvingUnmanagedDll += OnResolvingUnmanagedDll;
}
// 当LoadContext无法找到程序集时触发
protected override Assembly Load(AssemblyName assemblyName)
{
// 尝试通过插件自身的AssemblyDependencyResolver解析
string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
if (assemblyPath != null)
{
Console.WriteLine($"[PluginLoadContext] Loading '{assemblyName.Name}' from '{assemblyPath}' (plugin specific).");
return LoadFromAssemblyPath(assemblyPath);
}
// 如果插件的resolver找不到,则尝试从默认加载上下文加载
// 这对于共享的框架程序集(如System.*)或主应用程序提供的公共库非常重要
// 否则,每个插件都会尝试加载自己的System.Private.CoreLib,导致问题
var defaultAssembly = AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName);
if (defaultAssembly != null)
{
Console.WriteLine($"[PluginLoadContext] Loading '{assemblyName.Name}' from DefaultLoadContext (shared).");
return defaultAssembly;
}
Console.WriteLine($"[PluginLoadContext] Could not resolve assembly '{assemblyName.Name}'.");
return null; // 无法解析
}
// 处理非托管DLL的加载
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
string libPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (libPath != null)
{
Console.WriteLine($"[PluginLoadContext] Loading unmanaged DLL '{unmanagedDllName}' from '{libPath}' (plugin specific).");
return LoadUnmanagedDllFromPath(libPath);
}
// 尝试从默认加载上下文加载非托管DLL
var defaultLib = AssemblyLoadContext.Default.LoadUnmanagedDllFromAssemblyName(unmanagedDllName);
if (defaultLib != IntPtr.Zero)
{
Console.WriteLine($"[PluginLoadContext] Loading unmanaged DLL '{unmanagedDllName}' from DefaultLoadContext (shared).");
return defaultLib;
}
Console.WriteLine($"[PluginLoadContext] Could not resolve unmanaged DLL '{unmanagedDllName}'.");
return IntPtr.Zero;
}
// 额外的事件处理方法,通常在Load方法中已经处理了,但可以作为后备
private Assembly OnResolving(AssemblyLoadContext context, AssemblyName name)
{
// 这个方法会被Load方法调用,这里可以做一些额外的日志或诊断
return null;
}
private IntPtr OnResolvingUnmanagedDll(Assembly assembly, string unmanagedDllName)
{
// 这个方法会被LoadUnmanagedDll调用,这里可以做一些额外的日志或诊断
return IntPtr.Zero;
}
}
// 示例用法:
// string pluginDirectory = "/path/to/your/plugin"; // 假设这是插件的根目录
// try
// {
// var pluginContext = new PluginLoadContext(pluginDirectory);
// // 假设插件的主程序集名为 MyPlugin.dll
// var pluginAssembly = pluginContext.LoadFromAssemblyName(new AssemblyName("MyPlugin"));
//
// // 动态调用插件中的方法
// var pluginType = pluginAssembly.GetType("MyPlugin.PluginEntry");
// if (pluginType != null)
// {
// var instance = Activator.CreateInstance(pluginType);
// var method = pluginType.GetMethod("Run");
// method?.Invoke(instance, null);
// }
//
// // 如果需要卸载插件(仅当isCollectible为true时有效)
// // pluginContext.Unload();
// }
// catch (Exception ex)
// {
// Console.WriteLine($"Error loading plugin: {ex.Message}");
// }在这个例子中,每个
PluginLoadContext
AssemblyDependencyResolver
.deps.json
PluginLoadContext
_resolver
_resolver
AssemblyLoadContext.Default
除了插件场景,
AssemblyDependencyResolver
.deps.json
.deps.json
AssemblyDependencyResolver
总而言之,
AssemblyDependencyResolver
AssemblyLoadContext
以上就是.NET的AssemblyDependencyResolver如何解析依赖项?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号