Closed jsobell closed 7 years ago
Hello @jsobell ,
I'm not exactly sure to exactly understand this request. We currently don't support dynamic class creating but perhaps implementing something like the below example could work:
// Compile the function first
var compiled = Eval.Compile<Func<int, int, int>>("X + Y", "X", "Y");
// Register the function with a new namespace
EvalManager.RegisteredFunctions.Add("My.FunctionName", compiled);
// Default value could also be provided
// EvalManager.RegisteredFunctions.Add("My.FunctionName", compiled, new { X = 3});
// Execute a code, all function registered are now available through the namespace
Eval.Execute("My.FunctionName(1, 2)");
Let me know if something like this could work or you are looking for something else.
Best Regards,
Jonathan
The issue is that users often have features they want to use over and over again, so in a javascript evaluation system you'd typically define a set of helper function. In our system we have this:
var today = moment;
now = () => today();
minutes = (m) => today().add(m, 'minutes');
hours = (h) => today().add(h, 'hours');
daysathour = (d, h) => today().add(d, 'day').startOf('Day').add(h, 'hours');
AND = (...args) => args.every(x => x);
OR = (...args) => args.some(x => x);
IF = (cond, t, f) => cond ? t : f;
NOT = (expr) => !expr;
ISERR = (...args) => null;
Now these are created on a per-client basis, so traditionally developers will write this as a class, compile it, and add it as a reference to give the users access to the commands. What we would normally do for this is to run it through the .Net compiler at runtime, cache the assembly in a database image field, then reload it whenever a set of scripts is to run and add it to the newly created context. After that the Eval calls to all 200 guard conditions will have access to the same assembly code.
Obviously I can do this now by using the .Net runtime compiler, but I was wondering if any concepts existed in the current Eval-Expression system to avoid my having to do the compilation manually and add it through RegisterAssembly
?
There is some stuff that's possible for us to do.
By example, providing a way to add all code and only compile on our side a method on demand when first accessing.
However, we currently do not create an assembly so re-using an assembly would be impossible for our side.
Fair enough. As a quick test I added this:
static class EvalHelpers
{
public static EvalContext CompileAssembly(this EvalContext context, string code)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters {GenerateInMemory = true};
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.HasErrors)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError error in results.Errors)
{
sb.AppendLine($"Error ({error.ErrorNumber}): {error.ErrorText}");
}
throw new InvalidOperationException(sb.ToString());
}
context.RegisterAssembly(results.CompiledAssembly);
return context;
}
}
So if I create a set of utility features in code, such as
static string code = @"public static class Do { public static string It() { return ""Doing it!!!""; }} ";
I can include those in evaluations using
EvalContext context = new EvalContext();
context.CompileAssembly(code);
Console.WriteLine(context.Eval("Do.It()"));
Might be worth considering something along these lines.
Hi Johnathan, In one of my applications I use the Chrome ScriptV8 engine to provide powerful evaluation and scripting, so I'm looking at the feasibility of replacing it with this library. One feature is that because the ScriptV8's context is shared between many calls, when the 'Engine' is set up I can execute some predefined javascript function definitions, which the user has access to in the macros and conditions they define. For example, they might create a 'shared' library of javascript functions, and in each guard condition or evaluation process they can call those functions. In Eval-Expression we can obviously include a type or library, but normally only if it's been compiled into a DLL. What would be the best way to define a class in a string, and efficiently make that available for multiple calls in the current context? For example, I might have (just making this up)
This code is stored as a string, not compiled. Is there a way to have this code executed and the type registered in a context to it's always seen by the
Eval
?