Discussion: Provide an easier way to add MetaDataReferences from Types · Issue #65627 · dotnet/roslyn (original) (raw)

Background and Motivation

I hope this is in the right place for a discussion.

I have a small scripting library that is built ontop of Roslyn compiler libraries to dynamically compile C# code into assemblies/types.

One huge sticking point in usage of this tooling is the complexity of adding MetaDataReferences. There are a number of issues but there's at least one scenario that I have no solution for:

This came up from a user who was trying to use multiplate compilations and then use the output from prior compilations in another new compilation.

What doesn't work:

What does work (hack):

The issue here is that currently I use the assembly location to load the MetaDataReference:

var systemReference = MetadataReference.CreateFromFile(type.Assembly.Location);

This works with on-disk assemblies, but doesn't with in-memory assemblies (which actually live on disk?) which don't return a type.Assembly.Location. I could not figure out a way to create a MetaDataReference from the in-memory type (or assembly).

Specific question: Is there a way to get a MetaDataReference off an in-memory compiled type?

To give some context - this is roughly the code the user provided as a scenario. He basically is using multiple compilations of types with type 1 compiled and instantiated and then trying to pass this type 1 to a compilation of type 2.

The code fails because it can't resolve the MetaDataRefernce from the first type (in-memory compiled) as type.Assembly.Location which for the in-memory dll is blank. If I uncomment the line to explicitly write out an assembly to disk (script.OutputAssembly = "...") the code below works.

    [TestMethod]
    public void TwoDynamicClassesTest()
    {
        var class1Code = @"

using System;

namespace Test1 { public class Person { public string Name {get; set; } = ""Rick""; public string Description {get; set; } = ""Testing""; } } ";

        var class2Code = @"

using System; using Test1;

namespace Test {

public class Customer
{
    public Test1.Person CustomerInfo {get; set; } = new Test1.Person();
    public string CustomerNumber  { get; set; }         
} 

} ";

        var script = new CSharpScriptExecution();
        script.AddLoadedReferences();
        script.SaveGeneratedCode = true;
        script.GeneratedClassName = "__person";

// script.OutputAssembly = @"c:\temp\person.dll"; // if I do this it works

        var personType = script.CompileClassToType(class1Code);
        var person = Activator.CreateInstance(personType);


        Assert.IsNotNull(person, "Person should not be null. " + script.ErrorMessage + "\n" + script.GeneratedClassCodeWithLineNumbers);
        Console.WriteLine("Location: " + personType.Assembly.Location);
        
        //script = new CSharpScriptExecution();
        //script.AddDefaultReferencesAndNamespaces(); //AddLoadedReferences();
        //script.AddAssembly(script.OutputAssembly);
        
        script.SaveGeneratedCode = true;
        script.GeneratedClassName = "__customer";
        script.OutputAssembly = null;
        script.AddAssembly(personType);     // MetaDataReference.FromFile() in there
        var customerType = script.CompileClassToType(class2Code);

        Assert.IsNotNull(customerType, "Customer should not be null. " + script.ErrorMessage + "\n" + script.GeneratedClassCodeWithLineNumbers);
        Console.WriteLine(customerType);
        Console.WriteLine(customerType.Assembly.Location);

        dynamic customer = Activator.CreateInstance(customerType);

        
        Assert.IsNotNull(customer.CustomerInfo.Name, "Customer should not be null");
        Console.WriteLine(customer.CustomerInfo.Name);
    }

code link in Westwind.Scripting repo

Proposal

Overall creating and providing of MetaDataReferences is a pain in the butt currently. If you want to avoid loading the reference libraries runtime references of the .NET core libraries there's almost no rhyme or reason to what's included and what's not. The problem with the reference. Also these are not officially provided by Microsoft which seems odd (Jared Parsons maintains those).

It would be really helpful at minimum if MetaDataReference had some additional methods for loading a reference directly from a type so it can work of dynamically created types.