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

如何动态加载程序集及其依赖项且避免文件锁定?

动态加载程序集及其依赖项,避免程序集被进程锁定

我创建了一个包含三个项目的解决方案,场景如下:

1. Entities项目

包含Person类:

namespace Entities
{
    public class Person
    {
        public string Name { get; set; }
    }
}

2. Business项目

引用Entities项目,包含PersonBusiness类:

using Entities;

namespace Business
{
    public class PersonBusiness
    {
        public string GetPersonName(string name)
        {
            return name;
        }

        public Person GetPerson(string name)
        {
            Person person = new Person();
            person.Name = name;
            return person;
        }
     }
}

3. Runner项目

通过自定义AssemblyLoadContext动态加载Business.dll,代码如下:

using System.Runtime.Loader;

namespace Runner
{
    public class AssemblyLoader : AssemblyLoadContext
    {
        internal AssemblyLoader() : base(isCollectible: true)
        {

        }
    }

    internal class Assembly
    {
        private dynamic Instance { get; set; }
        private AssemblyLoader AssemblyLoader { get; set; }
        private Type AssemblyType { get; set; }

        public void Load()
        {
        
            AssemblyLoader = new AssemblyLoader();

            using (var fs = new FileStream(@"C:\temp\assemblies\business.dll", FileMode.Open, FileAccess.Read))
            {

                System.Reflection.Assembly assembly = AssemblyLoader.LoadFromStream(fs);

                AssemblyType = assembly.GetType("Business.PersonBusiness");

                Instance = Activator.CreateInstance(AssemblyType);

                Console.WriteLine(Instance.GetPersonName("Person name"));
                
                // 启用下方代码可运行但会锁定程序集
                //AssemblyLoadContext.Default.LoadFromAssemblyPath(@"C:\temp\assemblies\Entities.dll");

                Console.WriteLine(Instance.GetPerson("Person name 2").Name);

            }

        }

        public void Unload()
        {
            AssemblyLoader.Unload();

        }
    }
}

运行时错误

System.IO.FileNotFoundException: 'Could not load file or assembly 'Entities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

尝试用AssemblyLoadContext.Default.LoadFromAssemblyPath加载Entities.dll可正常运行,但会锁定该程序集文件,无法在运行时修改或删除。


解决方案

问题根源是自定义AssemblyLoader未处理依赖项加载逻辑:当Business.dll依赖Entities.dll时,默认会到AssemblyLoadContext.Default中查找,但自定义上下文未加载该依赖,因此抛出找不到的异常;而直接用Default上下文加载会因保持文件句柄导致锁定。

要实现无锁定加载,需让自定义上下文通过文件流加载依赖项,重写AssemblyLoadContext.Load方法即可实现:

1. 修改自定义AssemblyLoader类

using System.Linq;
using System.IO;

public class AssemblyLoader : AssemblyLoadContext
{
    private readonly string _assemblyDir;

    internal AssemblyLoader(string assemblyDir) : base(isCollectible: true)
    {
        _assemblyDir = assemblyDir;
    }

    protected override Assembly? Load(AssemblyName assemblyName)
    {
        // 优先检查当前上下文是否已加载该程序集
        var loadedAssembly = Assemblies.FirstOrDefault(a => a.GetName().Name == assemblyName.Name);
        if (loadedAssembly != null)
        {
            return loadedAssembly;
        }

        // 构建依赖程序集的路径
        string assemblyPath = Path.Combine(_assemblyDir, $"{assemblyName.Name}.dll");
        if (File.Exists(assemblyPath))
        {
            // 通过文件流加载,避免锁定文件
            using var fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read);
            return LoadFromStream(fs);
        }

        // 找不到则返回null,让系统尝试其他上下文加载
        return null;
    }
}

2. 修改Runner项目的Load方法

public void Load()
{
    string assemblyDir = @"C:\temp\assemblies";
    AssemblyLoader = new AssemblyLoader(assemblyDir);

    string businessDllPath = Path.Combine(assemblyDir, "business.dll");
    using (var fs = new FileStream(businessDllPath, FileMode.Open, FileAccess.Read))
    {
        System.Reflection.Assembly assembly = AssemblyLoader.LoadFromStream(fs);

        AssemblyType = assembly.GetType("Business.PersonBusiness");
        Instance = Activator.CreateInstance(AssemblyType);

        Console.WriteLine(Instance.GetPersonName("Person name"));
        Console.WriteLine(Instance.GetPerson("Person name 2").Name);
    }
}

核心原理

  • 重写Load方法后,自定义上下文会自行处理依赖项加载,通过文件流读取程序集,不会持有文件句柄,因此不会锁定文件;
  • 以isCollectible: true创建的上下文,调用Unload方法后,只要无残留引用,就能释放相关资源,程序集文件可随时修改或删除。

内容的提问来源于stack exchange,提问作者Eric Boeing

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 20:45:48