Roslyn Scripting

We recently needed to support local functions in runtime C# code. This runtime code is to allow us or the Customer to customise how a HTTP POST JSON document is constructed. At the time we were using CSScript to add runtime scripting to the application, however the default Evaluator was Mono which did not support local functions.

Fortunately Roslyn did. Due to that and some other requirements, we decided that we’d step away from CSScript and try and use Roslyn Scripting directly.

So here is the core of the code:

using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;

// ...

public static async Task<object> GenerateAsync(string codeAsString, GlobalData data)
{
    var options = ScriptOptions.Default
        .AddReferences("System", "System.Collections.Generic")
        .AddImports("System", "System.Collections.Generic");

    var script = CSharpScript.Create(codeAsString, options: options, globalsType: typeof(GlobalData));

    var state = await script.RunAsync(data);

    return (state.Exception == null) ? state.ReturnValue : throw new Exception("Runtime code had issues.", state.Exception);
}

Some comments on the code:

  • GlobalData type is the class that contains the variables that you want to expose to the runtime code. It needs to marked as public class. All its public properties are exposed to the script as global variables.
  • AddReferences adds namespaces/assemblies to the runtime code.
  • AddImports is effectively adding “using …” to the runtime code.

You can find a complete sample at roslyn-scripting-demo on GitHub.