.NET 8中AOT发布程序调用Assembly.LoadFile抛异常,如何动态加载
问题背景
一段简单的动态加载代码:
var r = Assembly.LoadFile("c:\\assembly.dll");
在未设置<PublishAot>true</PublishAot>发布时可正常运行,但开启AOT编译发布后,会抛出如下异常:
Unhandled Exception: System.PlatformNotSupportedException: Operation is not supported on this platform.
at Internal.Reflection.Execution.AssemblyBinderImplementation.Bind(String, AssemblyBindResult&, Exception&) + 0x34
at System.Reflection.Runtime.Assemblies.RuntimeAssemblyInfo.GetRuntimeAssemblyFromPath(String) + 0x4c
at System.Runtime.Loader.AssemblyLoadContext.LoadFromAssemblyPath(String) + 0x6d
at System.Reflection.Assembly.LoadFile(String) + 0x12c
at Program.$(String[] args) + 0x24
at app!+0x11b1c0
本文针对「AOT编译同时加载自定义程序集」的需求给出可行方案。
原因说明
.NET NativeAOT是将IL提前编译为原生机器码的编译模式,编译时会把所有依赖程序集整合到最终可执行文件中,并且移除了运行时JIT编译组件。而Assembly.LoadFile这类动态加载API依赖JIT来编译加载进来的IL代码,AOT环境下没有JIT支持,因此直接调用会抛出平台不支持异常。
可行解决方案
1. 使用NativeAOT插件机制(官方推荐)
.NET 7及以上版本的NativeAOT支持插件系统,允许AOT主程序加载同样经AOT编译的插件程序集:
- 把待加载的程序集做成AOT插件项目,项目文件配置:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <PublishAot>true</PublishAot> <IsAotPlugin>true</IsAotPlugin> </PropertyGroup> </Project> - 主程序中加载插件:
注意:插件必须和主程序使用相同.NET版本,且同样以AOT编译;插件中的调用逻辑需要提前在主程序中定义契约(如共享接口),否则反射调用会受限。using System.Runtime.Loader; var pluginPath = "c:\\assembly.dll"; var pluginAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(pluginPath); // 后续通过反射调用插件中的类型和方法(需提前定义共享契约,比如接口)
2. 改用ReadyToRun编译(兼容传统动态加载)
如果必须加载未经AOT编译的IL程序集,可以放弃完全NativeAOT,改用ReadyToRun编译——它会提前编译大部分代码,但保留JIT编译器用于动态加载场景:
<PropertyGroup> <PublishReadyToRun>true</PublishReadyToRun> <!-- 不要设置PublishAot --> </PropertyGroup>
缺点是生成的可执行文件体积更大、启动速度略慢,但能兼容Assembly.LoadFile这类传统动态加载API。
3. 预定义契约+代码生成(复杂场景适配)
若必须使用NativeAOT,且待加载程序集的逻辑结构固定,可以通过源码生成工具(如Source Generator)在编译阶段将插件逻辑整合到主程序中。这种方式灵活性较低,仅适用于插件逻辑可提前定义的场景。
总结
- 优先选择NativeAOT插件机制,这是官方为AOT场景提供的动态扩展方案;
- 若需加载普通IL程序集,改用ReadyToRun编译模式;
- 完全NativeAOT环境下无法直接加载未编译为原生码的IL程序集,因为JIT组件已被移除。
内容的提问来源于stack exchange,提问作者Starl1ght

