.NET 6中从runtimes文件夹加载平台特定DLL的问题
动态加载模块时System.DirectoryServices平台特定DLL未加载的问题分析与解决
问题根源
System.DirectoryServices 属于运行时特定的NuGet包,它的发布结构包含两部分:
- 根目录的占位DLL(如
System.DirectoryServices.dll):仅包含抛出PlatformNotSupportedException的空实现,用于在非目标平台上提示错误。 runtimes目录下的平台特定实现:比如runtimes/win-x64/lib/net6.0/System.DirectoryServices.dll,这才是Windows平台下的实际功能实现。
当主应用未引用该NuGet包时,.NET的程序集解析器没有内置规则去查找runtimes目录下的平台特定DLL——因为主应用的依赖清单和配置中不存在该包的运行时探测信息,解析器只会加载模块输出目录根下的占位DLL,进而触发异常。而主应用添加引用后,NuGet会自动生成绑定重定向和运行时探测配置,解析器才能找到正确的平台DLL。
解决方案
方案1:手动注册程序集解析事件(推荐)
在主应用启动时,注册程序集解析事件,当解析System.DirectoryServices相关程序集时,手动定位到runtimes目录下的平台特定文件。
以.NET 6+为例,代码示例:
using System.Reflection; using System.Runtime.InteropServices; using System.IO; // 在主应用初始化时注册 AssemblyLoadContext.Default.Resolving += (context, assemblyName) => { // 针对需要解析的目标程序集 var targetAssemblies = new[] { "System.DirectoryServices", "System.DirectoryServices.Protocols" }; if (!targetAssemblies.Contains(assemblyName.Name)) return null; // 获取当前运行时标识(如win-x64、win-x86) var runtimeId = RuntimeInformation.RuntimeIdentifier; // 加载模块并获取其所在目录 var moduleAssembly = Assembly.LoadFrom("模块路径/YourModule.dll"); var moduleDir = Path.GetDirectoryName(moduleAssembly.Location); // 拼接平台特定DLL的路径(替换为你的目标框架版本,如net7.0) var assemblyPath = Path.Combine( moduleDir, "runtimes", runtimeId, "lib", "net6.0", $"{assemblyName.Name}.dll" ); return File.Exists(assemblyPath) ? context.LoadFromAssemblyPath(assemblyPath) : null; };
方案2:使用独立的AssemblyLoadContext加载模块
为每个模块创建独立的AssemblyLoadContext,并配置其依赖搜索路径,让解析器优先查找runtimes目录下的文件:
var moduleDir = @"你的模块输出目录"; var runtimeId = RuntimeInformation.RuntimeIdentifier; // 创建独立的加载上下文 var moduleAlc = new AssemblyLoadContext("ModuleALC", isCollectible: true); // 注册解析逻辑,定向到平台特定目录 moduleAlc.Resolving += (context, assemblyName) => { var targetAssemblies = new[] { "System.DirectoryServices", "System.DirectoryServices.Protocols" }; if (!targetAssemblies.Contains(assemblyName.Name)) return null; var assemblyPath = Path.Combine( moduleDir, "runtimes", runtimeId, "lib", "net6.0", $"{assemblyName.Name}.dll" ); return File.Exists(assemblyPath) ? context.LoadFromAssemblyPath(assemblyPath) : null; }; // 加载模块 var moduleAssembly = moduleAlc.LoadFromAssemblyPath(Path.Combine(moduleDir, "YourModule.dll"));
这种方式的优势是隔离模块与主应用的依赖,避免全局解析逻辑的干扰。
方案3:主应用添加无复制的包引用(最简单)
如果可以接受主应用的项目文件中存在该包的引用(但不复制文件到输出目录),可以在主应用的csproj中添加:
<PackageReference Include="System.DirectoryServices" Version="你的版本号" PrivateAssets="All" />
PrivateAssets="All" 会让NuGet仅生成必要的运行时探测配置和绑定重定向,不会将该包的DLL复制到主应用输出目录。这样程序集解析器就能自动找到模块目录下runtimes中的平台DLL,同时主应用不会直接依赖该包的文件。
注意事项
- 确保模块项目的csproj中已设置
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>,这样NuGet包的runtimes目录会被复制到模块的输出目录。 - 替换代码中的目标框架版本(如
net6.0)为你实际使用的.NET版本。 - 若模块部署路径与主应用不同,需调整代码中的路径拼接逻辑,确保能正确定位到
runtimes目录。
内容的提问来源于stack exchange,提问作者Ondřej Daněk
相关产品推荐
相关产品推荐

