.NET 6迁移时Assembly.ReflectionOnlyLoad相关弃用方法的替代方案
.NET Core/.NET 5+ 已彻底移除仅反射加载(Reflection Only Load)上下文的底层实现,Assembly.ReflectionOnlyLoad()、Assembly.ReflectionOnlyLoadFrom() 两个API在.NET 6及以上版本标记为弃用且调用会抛出异常,针对你扫描程序集级别特性筛选待处理程序集的场景,有两种官方推荐的替代实现。
方案1:使用 MetadataLoadContext(推荐,完全匹配原仅反射加载场景)
MetadataLoadContext 是专门为只读元数据扫描设计的组件,不会将程序集加载到当前运行时执行上下文,不会触发程序集内任何代码(静态构造函数、模块初始化器等)的执行,安全性和性能都更优,是原仅反射加载API的官方直替方案。
使用前需要先引入NuGet包 System.Reflection.MetadataLoadContext,适配后的代码如下:
// 构造元数据解析器,需要同时传入待扫描程序集路径、当前运行时核心程序集路径用于依赖解析 var runtimeDllPaths = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll"); var resolver = new PathAssemblyResolver(assemblyFilenames.Concat(runtimeDllPaths)); // using包裹MetadataLoadContext,用完自动释放,不会锁定程序集文件、也不会残留元数据内存占用 using var metadataLoadCtx = new MetadataLoadContext(resolver); foreach (var assemblyFile in assemblyFilenames) { // 仅加载程序集元数据,无执行副作用 var metaAssembly = metadataLoadCtx.LoadFromAssemblyPath(Path.GetFullPath(assemblyFile)); if (FoundOurCustomAttr(metaAssembly)) { // 筛选通过的程序集再加载到运行时执行上下文做业务处理 var targetAssembly = Assembly.LoadFrom(assemblyFile); ProcessAssembly(targetAssembly); } }
注意:
MetadataLoadContext中读取到的Type、Attribute对象均为元数据层面的快照对象,和运行时加载的类型不共享,所以FoundOurCustomAttr方法判断特性时请通过特性类型全名称匹配,不要做类型强转,否则会抛出类型不匹配异常。
方案2:使用可收集的 AssemblyLoadContext 做隔离加载
如果所有待扫描程序集都是内部可信来源,不想额外引入NuGet依赖,可以用可收集的自定义AssemblyLoadContext做隔离加载,扫描完成后直接卸载上下文即可,不会长期占用内存或锁定文件,适配代码如下:
foreach (var assemblyFile in assemblyFilenames) { // 创建可收集的独立加载上下文,扫描完可完全卸载 using var scanAlc = new AssemblyLoadContext("AssemblyScanCtx", isCollectible: true); try { var scanAssembly = scanAlc.LoadFromAssemblyPath(Path.GetFullPath(assemblyFile)); if (FoundOurCustomAttr(scanAssembly)) { // 筛选通过的程序集加载到默认上下文执行业务 var targetAssembly = Assembly.LoadFrom(assemblyFile); ProcessAssembly(targetAssembly); } } finally { // 卸载扫描上下文,释放所有加载的非目标程序集 scanAlc.Unload(); } }
方案选择建议
- 待扫描程序集来源不完全可信、或待扫描程序集数量大时,优先选
MetadataLoadContext,无执行副作用、内存占用更低,是官方首推方案。 - 待扫描程序集均为内部可信、依赖关系简单时,可以选可收集
AssemblyLoadContext方案,代码改动量最小,不需要额外依赖。
原代码中挂载的AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve事件在.NET 5+中没有对应仅反射上下文的实现,使用上述两种方案时,分别通过PathAssemblyResolver、自定义AssemblyLoadContext的Resolving事件处理依赖解析即可。
内容的提问来源于stack exchange,提问作者Andrew Stephens

