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

如何使用AssemblyLoadContext替代Assembly.LoadFrom以避免程序集文件锁定并加载依赖项

如何使用AssemblyLoadContext替代Assembly.LoadFrom以避免程序集文件锁定并加载依赖项

我完全理解你遇到的困扰——Assembly.LoadFrom会直接锁定原DLL文件,导致后续无法运行或删除,而且xUnit的shadowCopy参数没生效,大概率是因为你提前手动加载了程序集,覆盖了xUnit的影子拷贝逻辑。用AssemblyLoadContext确实是当前.NET(Core/.NET 5+)里替代LoadFrom、实现程序集卸载和避免文件锁定的正确方式,我来一步步教你怎么改:

核心思路

  1. 用可收集的AssemblyLoadContext(设置isCollectible: true)来隔离测试程序集,这样用完可以主动卸载,释放资源。
  2. 通过读取文件流加载替代直接从路径加载,彻底避免锁定原文件(搭配FileShare.ReadWrite允许其他进程操作文件)。
  3. 自定义依赖解析逻辑,确保测试DLL的依赖也能正确加载且不被锁定。

修改后的代码示例

public IEnumerator<string> GetEnumerator()
{
    // 创建一个可收集的AssemblyLoadContext,用于隔离测试程序集
    var testLoadContext = new AssemblyLoadContext("TestAssemblyLoadContext", isCollectible: true);
    try
    {
        var dllDirectory = Path.GetDirectoryName(_pathToTestsDll);

        // 1. 读取测试DLL到内存流,避免锁定原文件
        using var testDllStream = new FileStream(
            _pathToTestsDll, 
            FileMode.Open, 
            FileAccess.Read, 
            FileShare.ReadWrite); // 允许其他进程读写原文件
        testLoadContext.LoadFromStream(testDllStream);

        // 2. 自定义依赖解析:从测试DLL所在目录加载依赖
        testLoadContext.Resolving += (context, assemblyName) =>
        {
            var dependencyPath = Path.Combine(dllDirectory, $"{assemblyName.Name}.dll");
            if (!File.Exists(dependencyPath))
                return null; // 找不到的话交给系统默认解析

            using var depStream = new FileStream(
                dependencyPath, 
                FileMode.Open, 
                FileAccess.Read, 
                FileShare.ReadWrite);
            return context.LoadFromStream(depStream);
        };

        // 3. 继续你的xUnit测试发现逻辑
        var controller = new XunitFrontController(AppDomainSupport.Denied, _pathToTestsDll);
        using var visitor = new TestDiscoverySink();
        controller.Find(false, visitor, TestFrameworkOptions.ForDiscovery());
        visitor.Finished.WaitOne();
        var tests = visitor.TestCases.Select(testCase => testCase.DisplayName).ToList(); 
        visitor.Finished.Dispose();

        return tests.GetEnumerator();
    }
    finally
    {
        // 卸载上下文,触发垃圾回收确保资源释放
        testLoadContext.Unload();
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

关键细节说明

  • 为什么用LoadFromStream?:直接用LoadFromAssemblyPath还是会短暂锁定文件,而读入内存流后加载,原文件完全不会被锁定,同时FileShare.ReadWrite允许其他进程随时修改或删除它。
  • 可收集上下文的限制:如果你的测试程序集包含非托管代码、COM互操作类型或某些特殊.NET特性,可收集上下文可能无法正常卸载,但常规的xUnit测试项目一般不会有这个问题。
  • 依赖解析逻辑:默认的AssemblyLoadContext会从应用基目录和GAC找依赖,但如果测试DLL的依赖和它在同一目录,就需要通过Resolving事件手动加载,避免找不到依赖的问题。
  • 为什么之前的shadowCopy没用?:你先调用了Assembly.LoadFrom直接加载了原文件,导致文件被锁定,xUnit的影子拷贝无法绕过这个锁定——现在去掉Assembly.LoadFrom的调用,改用内存流加载,xUnit的shadowCopy参数也能正常工作(不过我们的方案已经不需要它了)。

备注:内容来源于stack exchange,提问作者Marat Tim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.17 11:58:05