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

.NET Standard 2.0动态编译缺失引用问题求助

动态编译.NET Standard 2.0类库引用错误修复方案

问题核心分析

你的代码存在两个关键问题导致编译/加载异常:

  1. 重复添加引用程序集:同时调用.WithReferenceAssemblies(ReferenceAssemblyKind.NetStandard20)和.AddReferences(ReferenceAssemblies.NetStandard20),造成引用冗余冲突。
  2. 运行时依赖缺失:引用程序集仅用于编译阶段,动态加载的程序集在运行时无法找到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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 12:20:41