Closed axelriet closed 2 years ago
Hi Alex,
Indeed pragmatic assembly loading is a tricky objective to achieve. The problem is that neither Roslyn nor CLR are not particularly "collaborative" about that. In fact, Roslyn does not offer even any guideline for avoiding excessive assembly loading at all and instead simply demonstrates how to execute a script (any sort) and ... leave it in the memory forever.
CS-Script, understandably, is bound by these limitations. Though it offers a few measures to assist the users with what I would call "economical" assembly loading.
As the most direct measure you can always unload the script assembly after you are done with it:
dynamic script = CSScript.Evaluator
.With(eval => eval.IsAssemblyUnloadingEnabled = true)
.LoadMethod(@"public object func()
{
return new[] {0,5};
}");
var result = (int[])script.func();
var asm_type = (Type)script.GetType();
asm_type.Assembly.Unload();
For more than 15 years the code above was not possible but thanks to .NET Core 3 the assembly unloading became a reality. Though it comes with the price - referencing IsCollectible
assemblies by other assemblies is problematic. But if your scripts do not reference other scripts then you are fine.
There is an additional CS-Script own caching mechanism, which resembles Python caching. You were trying to use this approach. Though it was designed for caching the execution of the full-bodied scripts (.CompileCode(...)
) but not methods or delegates (your case). It also requires the assembly to be file- not memory-based. Otherwise, CLR will always treat it as a new assembly.
Thus you will need to create a small wrapper method to achieve the desired functionality:
dynamic load_method(string code)
{
var info = new CompileInfo { AssemblyFile = @$".\{method.GetHashCode()}.dll" };
Assembly asm = CSScript.RoslynEvaluator
.With(e => e.IsCachingEnabled = true)
.CompileCode(@"using System;
public class Script
{
" + code + @"
}",
info);
return asm.CreateObject("*");
}
. . .
var code = @"void Log(string message)
{
Console.WriteLine(message);
}";
var script1 = load_method(code);
var before = AppDomain.CurrentDomain.GetAssemblies().Count();
var script2 = load_method(code);
var after = AppDomain.CurrentDomain.GetAssemblies().Count();
I marked your issue as an "enhancement" as I believe the caching interface can be extended to cover delegates scenarios more naturally.
Done
The release v4.2.0 you can do caching for all IEvaluator.Compile*
and IEvaluator.Load*
and without the use of CompileInfo
Thanks Oleg! I'm using CS-Script Core 2.0.0, I'll try again.
Hi,
I'm just trying out CS script and I observed the following strange behavior on .NET 5.0: after executing the following line
CSScript.Evaluator.CreateDelegate("...")
All the assemblies on my app domain are loaded twice?
Repro:
75 assemblies loaded 168 assemblies loaded
This causes problems. Please advise.
Thanks, Axel