简体   繁体   中英

What's the best way to load a CSX script from a C# application?

Tools like dotnet-script and CSI allow users to write, compile, and run C# like "scripts" rather than including their code in a complete pre-compiled project. These tools work great for command-line usage, but don't offer much in terms of integrating dynamic C# "scripts" into a larger C# application.

If I have an existing C# application which wishes to load additional classes into its existing namespaces via.csx "scripts", how do I do that? Is it possible?

I guess you need to compile and execute your C# script.

In my experience, I used C# scripts by referencing Microsoft.CodeAnalysis.CSharp.Scripting (version 3.*) directly.

<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="3.*" />

Compilation

I suggest to use default compilation options:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Scripting;

// ...
ScriptOptions options = ScriptOptions.Default;

Maybe in the future, you'll need to add referenced assemblies to your script compilation.

So you need to compile your script (contained in a string variable in the code below):

byte[] assemblyBinaryContent;

var roslynScript = CSharpScript.Create(script, options);
var compilation = roslynScript.GetCompilation();

compilation = compilation.WithOptions(compilation.Options
    .WithOptimizationLevel(OptimizationLevel.Release)
    .WithOutputKind(OutputKind.DynamicallyLinkedLibrary));

using (var assemblyStream = new MemoryStream())
{
    var result = compilation.Emit(assemblyStream);
    if (!result.Success)
    {
        var errors = string.Join(Environment.NewLine, result.Diagnostics.Select(x => x));
        throw new Exception("Compilation errors: " + Environment.NewLine + errors);
    }

    assemblyBinaryContent = assemblyStream.ToArray();
}

GC.Collect(); // it allows to force clear compilation stuff.

Assembly assembly = Assembly.Load(assemblyBinaryContent);

var ret = Run(assembly); // see next paragraph

Execution

Obviously you need an entry point to execute your script. I found out this trickly solution. It works.

private object Run(Assembly assembly)
{
    //Execute the script
    var type = assembly.GetType("Submission#0");
    var method = type.GetMethod("<Factory>", BindingFlags.Static | BindingFlags.Public);

    var retTask = method.Invoke(null, new object[] { new object[2] }) as Task<object>;
    return retTask.GetAwaiter().GetResult();
}

I hope it can help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM