如何在 Roslyn 动态编译代码中引用另一个 DLL

And*_*rew 4 .net c# .net-core

我正在编写一个动态编译和执行 C# 代码的项目。问题是有时我希望代码调用另一个 DLL(为了这个示例,我将其称为“ANOTHER.DLL”)。它在 .Net 4.5 中工作正常,但在 .Net Core 中失败,我不明白为什么。任何帮助表示赞赏!

代码编译成功,但执行方法时出错。错误是:

FileNotFoundException:无法加载文件或程序集“ANOTHER,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null”。该系统找不到指定的文件。

ANOTHER.dll 位于同一/bin/debug文件夹中,并且绝对可以访问(代码可以编译!)

我注意到我可以通过向项目添加对 ANOTHER.DLL 的引用来解决该问题,但这违背了动态编译的目的。

我在 .Net Core 2.0 - 3.1 中尝试过这个

ANOTHER.DLL 是 .Net Standard 2.0(但与 .Net Standard 2.1 或 .Net Framework 的结果相同)。还尝试了各种版本的Microsoft.CodeAnalysis包,都给了我同样的错误。

var eval = new Evaluator();

string code = @"
    using System;
    namespace RoslynCompileSample
    {
        public class Test
        {
            public string Hello{
            get {
                //return ""Hello"";
                
                var c = new ANOTHER.Class1();
                return c.HelloWorld();
                }
            }
        }
    }";

SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);

var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
List < MetadataReference > references = new List < MetadataReference > ();

references.Add(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location));

string ReferenceList = "";
ReferenceList += "netstandard.dll\n";
ReferenceList += "System.Runtime.dll\n";
ReferenceList += "ANOTHER.dll\n";

string[] assemblies = ReferenceList.Split('\n');
foreach(string a in assemblies) {
  if (File.Exists(Path.Combine(assemblyPath, a.Trim()))) {
    references.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, a.Trim())));
  }
  else if (File.Exists(a.Trim())) {
    string currDirectory = Directory.GetCurrentDirectory();
    references.Add(MetadataReference.CreateFromFile(Path.Combine(currDirectory, a.Trim())));
  }
  else {
    string exepath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
    if (File.Exists(Path.Combine(exepath, a.Trim()))) {
      references.Add(MetadataReference.CreateFromFile(Path.Combine(exepath, a.Trim())));
    }
  }
}

CSharpCompilation compilation = CSharpCompilation.Create("assembly", syntaxTrees: new[] {
  syntaxTree
},
references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release));

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

  ms.Seek(0, SeekOrigin.Begin);
  assembly = Assembly.Load(ms.ToArray());
}

var type = assembly.GetType("RoslynCompileSample.Test");

var prop = type.GetProperties();
var all = prop.Where(x =>x.Name == "Hello");
var info = all.FirstOrDefault(x =>x.DeclaringType == type) ? ?all.First();

var method = info.GetGetMethod();

object obj;
obj = assembly.CreateInstance("RoslynCompileSample.Test");

object r = method.Invoke(obj, new object[] {}); // this is where the error occurs
Run Code Online (Sandbox Code Playgroud)

pwr*_*imo 8

解决方案基于我的要点

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            string code = @"
    using System;
    namespace RoslynCompileSample
    {
        public class Test
        {
            public string Hello{
            get {
                //return ""Hello"";
                
                var c = new ANOTHER.Class1();
                return c.HelloWorld();
                }
            }
        }
    }";
            var tree = SyntaxFactory.ParseSyntaxTree(code);
            string fileName = "mylib.dll";

            var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
            List<MetadataReference> references = new List<MetadataReference>();

            references.Add(MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location));
            references.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "netstandard.dll")));
            references.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")));
            references.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Private.CoreLib.dll")));
            var anotherDLLReference = MetadataReference.CreateFromFile(@"C:\Users\jjjjjjjjjjjj\source\repos\ConsoleApp2\ANOTHER\bin\Debug\netcoreapp3.1\ANOTHER.dll");

            references.Add(anotherDLLReference);
            var compilation = CSharpCompilation.Create(fileName)
              .WithOptions(
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
              .AddReferences(references)
              .AddSyntaxTrees(tree);
            string path = Path.Combine(Directory.GetCurrentDirectory(), fileName);
            EmitResult compilationResult = compilation.Emit(path);
            if (compilationResult.Success)
            {
                // Load the assembly
                Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);

                var type = assembly.GetType("RoslynCompileSample.Test");

                var prop = type.GetProperties();
                var all = prop.Where(x => x.Name == "Hello");
                var info = all.FirstOrDefault(x => x.DeclaringType == type) ?? all.First();

                var method = info.GetGetMethod();

                object obj;
                obj = assembly.CreateInstance("RoslynCompileSample.Test");

                object r = method.Invoke(obj, new object[] { });
            }

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

公平地说,我不知道它是如何工作的,因为我不熟悉在这个级别上使用程序集,但不知何故我设法摆脱了异常。

首先,我检查AssemblyLoadContext.Default了调试器。我注意到缺少对“ANOTHER.dll”的引用(尽管我们之前添加了它) 在此输入图像描述

然后我补充道AssemblyLoadContext.Default.LoadFromAssemblyPath(@"path to my ANOTHER.dll");。当我再次检查时 - ANOTHER.dll 就在那里。

在此输入图像描述

最后,我们可以看到我们的 hello world 消息

在此输入图像描述

所以我添加的代码基本上就是一行

// Load the assembly
Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);

var a = AssemblyLoadContext.Default.LoadFromAssemblyPath(@"C:\Users\jjjjjjjjjjjj\source\repos\ConsoleApp2\ANOTHER\bin\Debug\netcoreapp3.1\ANOTHER.dll");

var type = assembly.GetType("RoslynCompileSample.Test");
Run Code Online (Sandbox Code Playgroud)

这适用于针对 Standard 2.0 和 .NET Core 3.1 的 ANOTHER.dll

如果有人能真正告诉我们它是如何工作的,那就太好了。

  • 你是救星!太感谢了!我最终将 `AssemblyLoadContext.Default.LoadFromAssemblyPath();` 添加到添加到引用列表的每个程序集(即将它们加载到当前上下文,同时添加到引用列表进行编译)。也检查过,它也适用于 .Net Framework DLL,只需添加对“mscorlib.dll”的引用 (4认同)