NeyTI.EvalEngine.Core
0.9.2
dotnet add package NeyTI.EvalEngine.Core --version 0.9.2
NuGet\Install-Package NeyTI.EvalEngine.Core -Version 0.9.2
<PackageReference Include="NeyTI.EvalEngine.Core" Version="0.9.2" />
<PackageVersion Include="NeyTI.EvalEngine.Core" Version="0.9.2" />
<PackageReference Include="NeyTI.EvalEngine.Core" />
paket add NeyTI.EvalEngine.Core --version 0.9.2
#r "nuget: NeyTI.EvalEngine.Core, 0.9.2"
#:package NeyTI.EvalEngine.Core@0.9.2
#addin nuget:?package=NeyTI.EvalEngine.Core&version=0.9.2
#tool nuget:?package=NeyTI.EvalEngine.Core&version=0.9.2
EvalEngine
EvalEngine is an open-source C# expression and script evaluation library inspired by Z.Expressions.Eval, built from scratch on .NET 8 and Roslyn scripting.
Features
- Evaluate C# at runtime:
Eval.Execute<T>("1 + 2") - Run full statement scripts, not just expressions: locals,
if,switch, loops,return, andusingdirectives - Bind live instances: member access walks your real object graph, and mutations are seen on the next call
- Compile once: the cache key covers the expression and its binding shape, never parameter values, so one formula over a million records compiles a single time
- Precompile a formula set at startup with
Eval.Warmup, collecting every broken formula in one pass - Compile to any delegate signature with
Eval.Compile<T> - Security sandbox enforced on the compiled script before it runs
- Async evaluation with cancellation and an enforced timeout
- Dynamic LINQ:
WhereDynamic,SelectDynamic,OrderByDynamic,GroupByDynamic - String extensions:
"expr".EvalAs<T>(),"expr".CompileExpr<T>() - Runs on Windows, Linux, and macOS with identical results under any locale
Quick start
using NeyTI.EvalEngine.Core;
int sum = Eval.Execute<int>("1 + 2"); // 3
int result = Eval.Execute<int>("X + Y", new { X = 10, Y = 20 }); // 30
int value = "10 + 20".EvalAs<int>(); // 30
Statement scripts over live objects
var parameters = new Dictionary<string, object>
{
["Taxable"] = estate, // your entity, passed by reference
["Liquidation"] = liquidation,
};
decimal amount = Eval.Execute<decimal>("""
decimal valuation = Taxable.FiscalValuation;
decimal rate = 0;
if (Liquidation.Period.Year == 2026)
{
rate = 0.009m;
}
switch (Taxable.CategoryId)
{
case 2L: rate = 0.007m; break;
case 3L: rate = 0.008m; break;
default: rate = 0.011m; break;
}
decimal tax = valuation * rate;
return tax < 120000m ? 120000m : tax;
""", parameters);
Precompile at startup
var types = new Dictionary<string, Type> { ["Taxable"] = typeof(Estate), ["Liquidation"] = typeof(Liquidation) };
var result = Eval.WarmupTypes(await db.GetAllFormulas(), types, failFast: false);
foreach (var error in result.Errors)
log.LogWarning("Formula {Expression} will not compile: {Message}", error.Expression, error.Message);
Warmup needs the parameter names and types the real calls will use, because they are part of the cache key. See caching.
Reusable delegates
var compiled = Eval.Compile<Func<object?, object?>>("X * Y");
var r1 = compiled(new { X = 3, Y = 4 }); // 12
var add = Eval.Compile<Func<int, int, int>>("arg1 + arg2");
var r2 = add(3, 4); // 7
Scoped context and security
var ctx = Eval.CreateContext();
ctx.RegisterConstant("PI", 3.14159);
ctx.Options.SafeMode = SafeModeLevel.Sandboxed;
double area = Eval.Execute<double>("PI * 5 * 5", context: ctx);
// Throws EvalSecurityException before executing anything.
Eval.Execute<bool>("System.IO.File.Exists(\"x\")", null, ctx);
Types you bind as parameters stay allowed under a sandbox: exposing an instance is the host's decision.
Dynamic LINQ
using NeyTI.EvalEngine.Linq;
var filtered = people.WhereDynamic("it.Age > 18");
var ordered = people.OrderByDynamic("it.Name");
var grouped = people.GroupByDynamic("it.Category");
Logging
Silent unless you wire it up:
EvalLogging.LoggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
Migrating from Z.Expressions.Eval
Formulas written for a looser evaluator usually run unchanged. EvalOptions.AutoConvertTypes (on by default)
repairs the implicit numeric conversions strict C# rejects, so a stored decimal rate = 0.007; compiles as if
it read 0.007m. Repairs are driven by Roslyn's semantic model, so they work on machines with localized
compiler messages.
One difference to know about: context keys that are not valid C# identifiers, such as a pre-flattened
"Liquidation.Period.Year", are not bound. Reach the value through the live object instead.
See docs/compatibility.md for the full comparison.
Projects
| Project | Description |
|---|---|
NeyTI.EvalEngine.Abstractions |
Core interfaces, enums, options, and exceptions |
NeyTI.EvalEngine.Core |
Static facade, manager, execution context, string extensions |
NeyTI.EvalEngine.Compiler |
Roslyn pipeline: binder, cache, leniency, sandbox analyzer |
NeyTI.EvalEngine.Cache |
In-memory cache with expiration |
NeyTI.EvalEngine.Reflection |
Reflection metadata cache |
NeyTI.EvalEngine.Security |
Namespace, type, and assembly sandbox |
NeyTI.EvalEngine.Linq |
Dynamic LINQ operations |
Deployment
The engine compiles C# at runtime, which requires reference metadata on disk. Framework-dependent and self-contained deployments are supported; single-file publishing, NativeAOT, and aggressive trimming are not.
Documentation
See the docs folder for architecture, the compiler pipeline, the security model, caching, and performance.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.CodeAnalysis.CSharp.Scripting (>= 4.8.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- NeyTI.EvalEngine.Abstractions (>= 0.9.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
See docs/changelog.md. 0.9.2 keeps surrounding whitespace out of the cache key.