You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 13:51:19