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

引用非项目DLL时CSharpCompilation执行失败问题排查

C#动态编译依赖CsvHelper运行时异常问题解析

背景

我有一个需要编译执行的C#文件code.txt,它依赖NuGet的CsvHelper包。我准备了两个完全相同的项目,唯一区别是其中一个安装了CsvHelper NuGet包,另一个没有安装。

问题

  1. 安装了CsvHelper NuGet包的项目,编译后的代码可以正常执行;
  2. 未安装NuGet包的项目,仅手动引用所需的DLL及依赖DLL后,代码能成功编译,但运行时抛出以下异常:

FileNotFoundException: 无法加载文件或程序集'CsvHelper, Version=30.0.0.0, Culture=neutral, PublicKeyToken=8c4959082be5c823'或其依赖项,系统找不到指定文件。

无NuGet安装时的CSharpCompilation代码

MetadataReference[] References = new MetadataReference[]
        {
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Uri).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(System.Data.DataRow).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(System.Data.DataRowComparer).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(System.Drawing.Bitmap).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(System.Net.Http.HttpClient).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(System.Numerics.BigInteger).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(System.Windows.Forms.Button).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(System.Xml.XmlAttribute).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(System.Xml.Linq.XDocument).Assembly.Location),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\CsvHelper.dll")),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\Microsoft.Bcl.AsyncInterfaces.dll")),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\Microsoft.Bcl.HashCode.dll")),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\System.Buffers.dll")),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\System.Memory.dll")),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\System.Numerics.Vectors.dll")),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\System.Runtime.CompilerServices.Unsafe.dll")),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\System.Threading.Tasks.Extensions.dll")),
            MetadataReference.CreateFromFile(Path.GetFullPath(@"..\..\DLL's\System.ValueTuple.dll"))
        };

        string code = File.ReadAllText(Path.GetFullPath(@"..\..\Code.txt"));
        SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
        Compilation compilation = CSharpCompilation.Create(
            Path.GetRandomFileName(),
            new[] { syntaxTree },
            References,
            new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Debug
            ));

        using (var ms = new MemoryStream())
        {
            EmitResult result = compilation.Emit(ms);

            if (!result.Success)
            {
                IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
                    diagnostic.IsWarningAsError ||
                    diagnostic.Severity == DiagnosticSeverity.Error);

                foreach (Diagnostic diagnostic in failures)
                {
                    Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
                }
            }
            else
            {
                ms.Seek(0, SeekOrigin.Begin);
                Assembly assembly = Assembly.Load(ms.ToArray());

                Type type = assembly.GetType("NSImport.ImportTesting");
                object obj = Activator.CreateInstance(type);
                type.InvokeMember("FuncMain",
                    BindingFlags.Default | BindingFlags.InvokeMethod,
                    null,
                    obj,
                    new object[] { });
            }
        }

Code.txt代码

using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
using CsvHelper.Configuration;
using CsvHelper;

namespace NSImport
{
    internal class ImportTesting
    {
        public void FuncMain()
        {
            string filename = GetInputFileName();

            var configuration = new CsvConfiguration(CultureInfo.InvariantCulture)
            {
                Encoding = Encoding.UTF8,
                Delimiter = ",",
                HasHeaderRecord = true, //default value in any case
                Quote = '"'             //default value in any case
            };
        }

        private string GetInputFileName()
        {
            OpenFileDialog ofd = new OpenFileDialog()
            {
                InitialDirectory = @"C:\",
                Title = "Select input file",
                DefaultExt = "csv",
                Filter = "CSV File (*.csv)|*.csv|All Files (*.*)|*.*",
                FilterIndex = 1,
                CheckFileExists = true,
                CheckPathExists = true,
                Multiselect = false
            };

            if (ofd.ShowDialog() == DialogResult.OK)
                return ofd.FileName;
            else
                return string.Empty;
        }
    }
}

疑问解答

1. 为何引用的DLL需要成为运行应用的一部分?

C#编译阶段仅需依赖DLL提供的类型定义来校验代码语法和类型合法性,不会把依赖DLL的实现逻辑嵌入到你的输出程序集里。运行时CLR需要加载这些依赖DLL,才能找到代码中调用的类、方法的具体实现,找不到就会抛出文件缺失异常。简单说:编译看定义,运行要实现。

2. 是否可以在不将引用DLL纳入项目的情况下执行代码?

可以,有两种可行方案:

  • 合并程序集:用ILMerge、Costura.Fody这类工具把依赖DLL的内容嵌入到你的输出程序集里,运行时无需额外的DLL文件;
  • 指定加载路径:确保CLR能在运行时找到DLL,比如把DLL复制到程序工作目录、注册到全局程序集缓存(GAC),或者通过AppDomain.AssemblyResolve事件手动从指定路径加载DLL。

你当前的问题是编译时引用了DLL,但运行时这些DLL不在CLR的自动查找路径里,所以报错。如果在加载动态编译的程序集前,先把依赖DLL加载到内存,或者把DLL复制到当前工作目录,就能解决问题。

3. 针对模糊的FileNotFoundException,最佳排查方式是什么?

  • 启用程序集绑定日志:打开Visual Studio开发者命令提示符,运行fuslogvw.exe(程序集绑定日志查看器),配置记录失败的绑定请求。它会详细显示CLR尝试加载的程序集版本、查找路径、失败原因,是最直接的排查工具;
  • 校验DLL版本与签名:确认手动引用的CsvHelper.dll版本是30.0.0.0,且公钥Token与异常信息一致,避免版本不匹配或使用未签名的版本;
  • 检查工作目录与DLL路径:用Directory.GetCurrentDirectory()确认程序运行时的工作目录,验证DLL的实际路径是否正确、文件是否存在;
  • 手动加载测试:在代码中先调用Assembly.LoadFrom(@"DLL路径\CsvHelper.dll"),如果加载失败会抛出更具体的异常,比如依赖项缺失、版本不兼容等。

内容的提问来源于stack exchange,提问作者Sigh-AniDe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:02:34