1

I'm using Roslyn to compile strings of C# code. I'd like to use the output of one of the compilations as metadata reference for another, but when I try to invoke a method that forces the loading of the referenced assembly, I get an ImageNotFoundException, complaining that the referenced assembly (MyReference.dll in the sample case) could not be found. This is the minified version of my problem:

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);
    }
}

The references I use:

  • <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0-1.final" /> (Roslyn itself)
  • <PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.1" /> (to not have to deal with metadata management of .NET Core)

Without referencing anything, a hello world program compiles fine, making me believe that there is some runtime quirk I do not know about, like requiring the file to be on disk or something. However, I could not verify this.

1 Answer 1

1

As it turns out, the runtime simply does not know where to locate the assembly. While the other metadata references are in well-known places, MyReference.dll does not exist anywhere other than in-memory. I suppose this is because the runtime does not really care about where a metadata reference was read from, but I'm really not sure.

The solution is simple, one can just create an AssemblyLoadContext and manually point at the correct bytes:

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);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.