.NET 6加载互相引用的程序集时遇FileNotFoundException如何解决
问题描述
我有一个C# .NET 6项目,在进程内编译两个程序集,其中一个程序集引用另一个。当我创建引用其他程序集的对象实例,或尝试调用其方法时,收到以下错误:
System.IO.FileNotFoundException: 'Could not load file or assembly 'mydll.dll, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified
通过AppDomain.CurrentDomain.GetAssemblies()返回的列表可以确认两个程序集都已加载,但仍报错。
尝试过两种实现方式,均出现错误:
方式一:使用AppDomain.CurrentDomain.CreateInstance
var assembly = AppDomain.CurrentDomain.Load(assemblyData); // 字节数组形式的程序集数据 foreach (var library in buildState.AllReferences) { AppDomain.CurrentDomain.Load(library.CompiledData); // 加载引用的程序集字节数组 } var assemblies = AppDomain.CurrentDomain.GetAssemblies(); // 确认两个程序集已加载 // 错误在此行抛出 var runner = AppDomain.CurrentDomain.CreateInstance($"{@namespace}.dll", $"{@namespace}.{className}").Unwrap() as ITemplateRunner; if (runner == null) throw new Exception($"{className} is not a ITemplateRunner"); await runner.Execute();
方式二:使用Activator.CreateInstance
var assembly = Assembly.Load(assemblyData); foreach (var library in buildState.AllReferences) { var t = Assembly.Load(library.CompiledData); } var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var runner = Activator.CreateInstance(assembly.GetType($"{@namespace}.{className}") ?? throw new Exception($"{@namespace}.{className} not in compiled dll.")) as ITemplateRunner; if (runner == null) throw new Exception($"{className} is not a ITemplateRunner"); // 错误在此行抛出 await runner.Execute();
编辑1:调用assembly.CreateInstance()时,两种方式都会出现相同的错误提示。
解决方案
核心问题是:即便通过字节数组加载了程序集,CLR解析依赖时仍会优先尝试从磁盘路径查找,而非直接使用内存中已加载的程序集。以下是几种可行的解决方法:
1. 注册AssemblyResolve事件
在加载程序集前,给当前AppDomain注册AssemblyResolve事件,当CLR找不到程序集时,从已加载的集合中匹配返回:
// 先注册事件,再加载程序集 AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { return AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.FullName == args.Name); }; // 加载程序集逻辑不变 var assembly = Assembly.Load(assemblyData); foreach (var library in buildState.AllReferences) { Assembly.Load(library.CompiledData); } // 创建实例 var runner = Activator.CreateInstance(assembly.GetType($"{@namespace}.{className}")) as ITemplateRunner; // ...后续逻辑
2. 使用AssemblyLoadContext(.NET 6+推荐)
.NET Core及后续版本推荐用AssemblyLoadContext管理内存程序集,它能自动处理依赖解析,更适配现代.NET架构:
var loadContext = new AssemblyLoadContext(null, isCollectible: false); // 先加载依赖程序集 foreach (var library in buildState.AllReferences) { using var stream = new MemoryStream(library.CompiledData); loadContext.LoadFromStream(stream); } // 再加载主程序集 using var mainStream = new MemoryStream(assemblyData); var assembly = loadContext.LoadFromStream(mainStream); // 创建实例 var runner = Activator.CreateInstance(assembly.GetType($"{@namespace}.{className}")) as ITemplateRunner; // ...后续逻辑
3. 将程序集写入临时目录
如果上述方法不适用,可把字节数组写入临时文件夹,让CLR能从磁盘找到程序集:
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(tempDir); // 写入依赖程序集 foreach (var library in buildState.AllReferences) { var dllPath = Path.Combine(tempDir, $"{library.AssemblyName}.dll"); File.WriteAllBytes(dllPath, library.CompiledData); } // 写入主程序集 var mainDllPath = Path.Combine(tempDir, $"{mainAssemblyName}.dll"); File.WriteAllBytes(mainDllPath, assemblyData); // 从磁盘加载程序集 var assembly = Assembly.LoadFrom(mainDllPath); var runner = Activator.CreateInstance(assembly.GetType($"{@namespace}.{className}")) as ITemplateRunner; // ...后续逻辑 // 程序结束后可清理临时文件 // Directory.Delete(tempDir, recursive: true);
内容的提问来源于stack exchange,提问作者BJury
相关产品推荐
相关产品推荐

