I am trying to use Roslyn to compile C# code at runtime. My compilation function is as follows:
using System.Collections.Immutable;
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
namespace RuntimeCompiler;
public static class RuntimeCompiler
{
public static void Compile(string sourceCode, string outputPath, string assemblyName="MyAssembly")
{
var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
var compilation = CSharpCompilation.Create(assemblyName)
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
.AddSyntaxTrees(syntaxTree);
using var stream = new FileStream(outputPath, FileMode.Create);
var result = compilation.Emit(stream);
if (result.Success) return;
throw new Exception($"Compilation failed");
}
}
and my driver function is below:
namespace RuntimeCompiler;
public class main
{
public static void Main()
{
const string sourceCode = @"
namespace UtilityLibraries;
public static class StringLibrary
{
public static bool StartsWithUpper(this string? str)
{
if (string.IsNullOrWhiteSpace(str))
return false;
char ch = str[0];
return char.IsUpper(ch);
}
}";
const string outputPath = @"/tmp/mylib3.dll";
RuntimeCompiler.Compile(sourceCode, outputPath, "ass3");
}
}
The code works, and I can see my dll being generated. The problem is, when I try to use the dll in a separate ConsoleApp like below:
using UtilityLibraries;
StringLibrary.StartsWithUpper("asdasd");
Compiler complains:
The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'.
I also examined the value of typeof(object).Assembly.Location, it is /usr/share/dotnet/shared/Microsoft.NETCore.App/6.0.25/System.Private.CoreLib.dll so it appears to me that the requested assembly is already added.
Any thoughts?
(In case anyone is interested in taking a closer look, I uploaded a minimally reproducible example here)
The problem you have is related to what assemblies you are adding. Start from this comment @github:
And then check the Runtime-code-generation-using-Roslyn-compilations-in-.NET-Core-App.md doc. What you are actually doing is compiling against runtime (implementation) assemblies which breaks adding compiled dll as reference to the project. You want to compile against reference ones to do that:
I have changed 2 things to make the code to work:
Notes
I would argue that this is a bit unconventional approach to the problem. If you want "dynamic"/build-time code generation - consider using source generators
If source generator is not an option - consider generating a project and compiling a whole project
If you still want to utilize your way you can try something like the following (see this comment @github):
Add to the generator .csproj:
Add
Microsoft.Extensions.DependencyModelnuget and use it to get the references:Also you can try compiling against .NET Standard references.
Your current approach allows to load assembly dynamically and call methods with reflection. For example: