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:
- Dynamically compile a C# class and get back an assembly and type
- Type instantiates fine at this point - all good
- Now create a second dynamic C# class and compile
- How do I get the first type's meta data reference into the second compilation?
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:
- Trying to get a MetaDataReference from an in-memory generated type
What does work (hack):
- Get a MetaDataReference from an on-disk compiled type
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.