代码之家  ›  专栏  ›  技术社区  ›  Peter Lenkefi

在.NET中使用内存中程序集作为另一个内存中程序集中的元数据引用

  •  0
  • Peter Lenkefi  · 技术社区  · 3 年前

    我正在使用Roslyn编译C#代码的字符串。我想使用其中一个编译的输出作为另一个编译中的元数据引用,但当我试图调用一个强制加载被引用程序集的方法时,我会得到一个 ImageNotFoundException ,抱怨引用的程序集( MyReference.dll 在示例情况下)。这是我的问题的缩小版:

    internal class Program
    {
        public static MemoryStream CompileCSharp(
            string assemblyName,
            string code,
            params MetadataReference[] additionalReferences)
        {
            var sourceText = SourceText.From(code, Encoding.UTF8);
            var tree = SyntaxFactory.ParseSyntaxTree(sourceText);
    
            var defaultReferences = Basic.Reference.Assemblies.Net70.ReferenceInfos.All
                .Select(r => MetadataReference.CreateFromStream(new MemoryStream(r.ImageBytes)));
    
            var compilation = CSharpCompilation.Create(
                assemblyName: assemblyName,
                syntaxTrees: new[] { tree },
                references: defaultReferences.Concat(additionalReferences),
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
    
            var stream = new MemoryStream();
            var emitResult = compilation.Emit(stream);
            Debug.Assert(emitResult.Success);
    
            stream.Position = 0;
            return stream;
        }
    
        static void Main(string[] args)
        {
            var referenceBytes = CompileCSharp(
                "MyReference.dll",
                """
                    public class IdentityProvider
                    {
                        public T Identity<T>(T x) => x;
                    }
                    """);
            var reference = MetadataReference.CreateFromStream(referenceBytes);
    
            var mainAssembly = CompileCSharp(
                "Main.dll",
                """
                    using System;
    
                    public class Program
                    {
                        public static void Main()
                        {
                            var provider = new IdentityProvider();
                            Console.WriteLine(provider.Identity(123));
                        }
                    }
                    """,
                reference);
    
            var asm = Assembly.Load(mainAssembly.ToArray());
            asm.GetType("Program")!.GetMethod("Main")!.Invoke(null, null);
        }
    }
    

    我使用的参考文献:

    • <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0-1.final" /> (罗斯林本人)
    • <PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.1" /> (不必处理.NET Core的元数据管理)

    在不引用任何内容的情况下,helloworld程序编译得很好,这让我相信有一些我不知道的运行时怪癖,比如要求文件在磁盘上或其他什么地方。然而,我无法证实这一点。

    0 回复  |  直到 3 年前
        1
  •  1
  •   Peter Lenkefi    3 年前

    事实证明,运行时根本不知道在哪里可以找到程序集。虽然其他元数据引用在众所周知的地方, MyReference.dll 不存在于内存以外的任何位置。我想这是因为运行时并不真正关心元数据引用是从哪里读取的,但我真的不确定。

    解决方案很简单,只需创建 AssemblyLoadContext 并手动指向正确的字节:

    var loadContext = new AssemblyLoadContext("testLoadContext");
    loadContext.Resolving += (loader, name) =>
    {
        if (name.Name!.Contains("MyReference.dll"))
        {
            return Assembly.Load(referenceBytes.ToArray());
        }
        return null;
    };
    
    var asm = loadContext.LoadFromStream(mainAssembly);
    asm.GetType("Program")!.GetMethod("Main")!.Invoke(null, null);
    
    推荐文章