.NET Standard 2.0动态编译缺失引用问题求助
动态编译.NET Standard 2.0类库引用错误修复方案
问题核心分析
你的代码存在两个关键问题导致编译/加载异常:
- 重复添加引用程序集:同时调用
.WithReferenceAssemblies(ReferenceAssemblyKind.NetStandard20)和.AddReferences(ReferenceAssemblies.NetStandard20),造成引用冗余冲突。 - 运行时依赖缺失:引用程序集仅用于编译阶段,动态加载的程序集在运行时无法找到
System.Linq.Expressions等类型的实现程序集,触发assembly.GetTypes()异常。
分步修复方案
1. 清理重复引用配置
修改Executor.Execute方法中的编译逻辑,移除重复的引用添加代码。.WithReferenceAssemblies会自动注入.NET Standard 2.0所需的全部引用程序集,无需额外调用.AddReferences(ReferenceAssemblies.NetStandard20):
var compilation = CSharpCompilation.Create(assemblyName: Path.GetRandomFileName()) .WithReferenceAssemblies(ReferenceAssemblyKind.NetStandard20) .AddReferences(MetadataReference.CreateFromFile(typeof(ITask).Assembly.Location)) .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) .AddSyntaxTrees(syntaxTree);
2. 添加运行时依赖解析逻辑
在Executor类中添加静态构造函数,注册AssemblyResolve事件,帮助CLR从当前应用域已加载的程序集中查找缺失的实现依赖:
static Executor() { AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var assemblyName = new AssemblyName(args.Name); return AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.GetName().Name == assemblyName.Name); }; }
3. 优化错误提示与类型加载逻辑
替换模糊的异常抛出方式,增加类型查找的显式校验,提升调试效率:
// 编译错误处理优化 if (!result.Success) { var errors = string.Join(Environment.NewLine, result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)); throw new InvalidOperationException($"编译失败:{Environment.NewLine}{errors}"); } // 类型加载优化 var taskType = assembly.GetType("Consumer.MyTask"); if (taskType == null) { throw new InvalidOperationException("未找到Consumer.MyTask类型"); } var task = Activator.CreateInstance(taskType); var runMethod = taskType.GetMethod("Run"); runMethod?.Invoke(task, null);
完整修复后的Executor代码
using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System.Reflection; using System.Linq.Expressions; using System.Linq; namespace DynamicCompilationNetStandard2._0; public static class Executor { static Executor() { AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => { var assemblyName = new AssemblyName(args.Name); return AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.GetName().Name == assemblyName.Name); }; } public static void Execute(string source) { var syntaxTree = SyntaxFactory.ParseSyntaxTree(source); var compilation = CSharpCompilation.Create(assemblyName: Path.GetRandomFileName()) .WithReferenceAssemblies(ReferenceAssemblyKind.NetStandard20) .AddReferences(MetadataReference.CreateFromFile(typeof(ITask).Assembly.Location)) .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) .AddSyntaxTrees(syntaxTree); using (var ms = new MemoryStream()) { var result = compilation.Emit(ms); if (!result.Success) { var errors = string.Join(Environment.NewLine, result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error)); throw new InvalidOperationException($"编译失败:{Environment.NewLine}{errors}"); } ms.Seek(0, SeekOrigin.Begin); var assembly = Assembly.Load(ms.ToArray()); try { _ = assembly.GetTypes(); } catch (Exception ex) { throw new InvalidOperationException("加载程序集类型失败", ex); } var taskType = assembly.GetType("Consumer.MyTask"); if (taskType == null) { throw new InvalidOperationException("未找到Consumer.MyTask类型"); } var task = Activator.CreateInstance(taskType); var runMethod = taskType.GetMethod("Run"); runMethod?.Invoke(task, null); } } } public interface ITask { void CanRun<T>(Expression<Func<T, bool>> predicate); void Run(); }
额外说明
basic-reference-assemblies的核心作用是隔离编译时引用与运行时实现,避免绑定到特定.NET版本,此方案保留了该特性。- 改用反射调用替代
dynamic类型,可获得更好的类型安全性和调试体验。
内容的提问来源于stack exchange,提问作者Adam Paquette
相关产品推荐
相关产品推荐

