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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NeyTI.EvalEngine.Core" Version="0.9.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NeyTI.EvalEngine.Core" Version="0.9.2" />
                    
Directory.Packages.props
<PackageReference Include="NeyTI.EvalEngine.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NeyTI.EvalEngine.Core --version 0.9.2
                    
#r "nuget: NeyTI.EvalEngine.Core, 0.9.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NeyTI.EvalEngine.Core@0.9.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NeyTI.EvalEngine.Core&version=0.9.2
                    
Install as a Cake Addin
#tool nuget:?package=NeyTI.EvalEngine.Core&version=0.9.2
                    
Install as a Cake Tool

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, and using directives
  • 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.2 88 9/23/2026
0.9.1 94 9/22/2026
0.8.0 91 9/22/2026
0.7.0 111 9/9/2026
0.6.0 97 9/1/2026

See docs/changelog.md. 0.9.2 keeps surrounding whitespace out of the cache key.