I'm trying to create a reusable .NET Standard 2.0 library that uses Roslyn to dynamically compile code at runtime to an in-memory assembly. This dynamically created assembly contains classes that derive from a base class that is part of the library. I instantiate them via reflection in the apps that reference the library. The project structure looks like this:
Suppose that I have the following type in my netstandard2.0 library:
namespace MyLibrary
{
public abstract class BaseClass
{
public abstract int CalculateSomething();
}
}
I then create the following unit test in a .NET Core 2.2 project:
namespace NetCore2_2.Tests
{
public static class RoslynTests
{
[Fact]
public static void CompileDynamicallyAndInvoke()
{
// Create syntax tree with simple class
var syntaxTree = CSharpSyntaxTree.ParseText(@"
using System;
using MyLibrary;
namespace Foo
{
public sealed class Bar : BaseClass
{
public override int CalculateSomething()
{
return (int) Math.Sqrt(42);
}
}
}");
// Create compilation, include syntax tree and reference to core lib
var compilation = CSharpCompilation.Create(
"MyDynamicAssembly.dll",
new[] { syntaxTree },
new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(BaseClass).Assembly.Location)
},
new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release)
);
// Compile it to a memory stream
var memoryStream = new MemoryStream();
var result = compilation.Emit(memoryStream);
// If it was not successful, throw an exception to fail the test
if (!result.Success)
{
var stringBuilder = new StringBuilder();
foreach (var diagnostic in result.Diagnostics)
{
stringBuilder.AppendLine(diagnostic.ToString());
}
throw new XunitException(stringBuilder.ToString());
}
// Otherwise load the assembly, instantiate the type via reflection and call CalculateSomething
var dynamicallyCompiledAssembly = Assembly.Load(memoryStream.ToArray());
var type = dynamicallyCompiledAssembly.GetType("Foo.Bar");
var instance = (BaseClass) Activator.CreateInstance(type);
int number = instance.CalculateSomething();
Assert.Equal((int) Math.Sqrt(42), number);
}
}
}
In this test, I first parse a piece of C# code that derives from BaseClass
in the netstandard2.0 library. This piece of code additionally references System.Math
. I then create a C# compilation object that includes references to the core lib (of .NET Core 2.2) and my library. This compilation object emits the DLL to a memory stream. If compiling fails, the test will fail with an exception that contains all diagnostics.
This unit test does fail with the following error message:
(7,31): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.
(11,26): error CS0012: The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'.
I have the following questions:
- Is this not working because the Roslyn NuGet package is referenced in a .NET Standard 2.0 project and thus always tries to compile to the netstandard2.0 Target Framework Moniker? I suspect that netstandard2.0 has a different definition of
System.Object
that forwards to the actual implementation of the target platform. And this forwarding definition is not referenced in my compilation unit. - Is there a way to change the target framework? I looked at
CSharpCompilationOptions
andEmitOptions
, but couldn't find anything that let's me change the target framework. - Do I maybe need to use another Roslyn NuGet package such as Microsoft.Net.Compilers.Toolset? I try to avoid this because actually want to use the default compilers instead of the ones in the NuGet package.